Python标准库threading

人工智能炼丹师
2019-03-24 / 0 评论 / 169 阅读 / 正在检测是否收录...

threading 用来管理一个进程中的并行操作(线程)

在python中简单使用线程

import threading

def worker():
    print('Worker')

threads = []
for i in range(5):
    t = threading.Thread(target = worker)
    threads.append(t)
    t.start()

target参数是线程调用的函数,threading.Thread还可以通过name指定线程名称,args指定调用函数传入的参数。
start()函数,开始执行线程。另外,theading 中daemon(设置线程是否为后台线程)、join(后台线程阻塞)也是常用的用法。此处就不一一举例了。

线程同步

多线程的程序面临一个重要的问题,由于线程之间的资源是共享的,当不同线程访问共享资源时可能出现访问冲突(例如消费者和生产者的执行)。另外,当线程之间可能需要等待某个事件发生后,才被激活而执行(例如行人、车辆必须等待绿灯才能通过) theading中有lock、Rlock、condtion、Semaphore、Event实现线程的同步,当然各个方法还是有不同的特点。

首先,我们通过一个简单的栗子来感受下,多线程不进行同步可能出现的问题。

import threading
import time

count =0 

def addcount():
    global count
    for i in range(1000):
        count += 1
for i in range(10):
    t = threading.Thread(target=addcount)
    t.start()
time.sleep(3)
print count

理论上上述代码的结果应该是1000*10,但是当我们运行程序发现每次的结果都不同,并且结果都小于10000。对全局变量的修改可以分为取值和赋值两个阶段,假设某一时刻count=100,如果按照方式①Thread1取值(temp1=100)-->Thread1赋值(count=temp1+1)-->Thread2取值(temp2=101)-->Thread2赋值(count=temp2+1)这种方式,运行结果count为102。但是如果线程的顺序变为方式②Thread1取值(temp1=100)-->Thread2取值(temp2=100)-->Thread1赋值(count=temp1+1)-->Thread2赋值(count=temp2+1),运行结果count为101。
如果想要使得程序严格按照方式①的顺序运行,我们就需要进行线程的同步。

lock

lock(锁)的工作原理: 所有的线程只有一把锁,谁抢到就执行哪个线程,当拥有锁的线程结束操作,需要释放锁让别的线程去竞争。
我们可以对上述的代码进行改写

import threading
import time

count =0 
lock = threading.Lock()

def addcount():
    global count
    for i in range(1000):
        lock.acquire()
        count += 1
        lock.release()

for i in range(10):
    t = threading.Thread(target=addcount)
    t.start()
time.sleep(3)
print count

与原来的代码不同之处仅仅在于获取锁acquire()、释放锁release()的两个操作而已,Lock.acquire(blocking=True, timeout=-1),blocking参数表示是否阻塞当前线程等待,timeout表示阻塞时的等待时间
threading 中还有RLock与Lock功能类似,但是在同一个线程内可以多次调用RLCOK.acquire来获取锁,Lock如果多次accuire则会出现死锁的情况。
这样的RLock对比与Lock在哪些问题上有优势呢?(TO DO)

GIL(Global Interpreter Lock)

GIL 全局解释锁(TO DO)

condtion

condition 提供了比Lock更复杂的功能。线程只有满足一定条件后才能去竞争锁,不满足条件的线程处于等待池(当前线程调用condtion.wait()进入等待),直到收到通知(其它线程调用condition.notify())才退出等待状态,再开始去竞争锁。
下面以消费者和生产者为例(始终保证生产足够,但不过剩(产品数量限制在30以内):) )

import threading 
import time

condition = threading.Condition()
class Producer(threading.Thread):
    def run(self):
        while(1):
            global x
            print 'producer accquire lock'
            condition.acquire()
            print 'producer get lock'

            if x > 30:
                print 'x>need_max producer wait,and release lock'
                condition.wait()
            else:
                x = x + 5
                print 'producing',x
                condition.notify()
                print 'producer done, notify others'
            condition.release()
            #print 'producer release lock'
            time.sleep(1) #暂停线程,避免与consumer争lock

class Consumer(threading.Thread):
    def run(self):
        while(1):
            global x
            print 'consumer accquire lock'
            condition.acquire()
            print 'Consumer get lock'

            if x == 0:
                print 'x==0 consumer wait,and release lock'
                condition.wait() 
            else:
                x = x - 1
                print 'consuming ',x
                time.sleep(1)    
                condition.notify()
                print 'consumer done, notify others'
            condition.release()
            #print 'consumer release lock'
            time.sleep(1) 
x = 5
p = Producer()
c = Consumer()

p.start()
c.start()

上述代码首先从Thread中继承得到两个子类。线程Thread类的继承只需要实现run方法即可。run方法在线程执行start启动线程被调用。Producer与Consumer的结构基本相同,只是判断进入wait或notify的条件不一样而已。当生产者当生产的量大于一定的阈值,调用wait进入等待状态,此时Consumer获得lock,消耗一个产品,并通知生产者继续生产。执行的结果如下所示,产品x的数量始终保持在30左右。

Consumer get lock
consuming  22
consumer done, notify others
producer get lock
producing 27
producer done, notify others
producer accquire lockconsumer accquire lock

producer get lock
producing 32
producer done, notify others
Consumer get lock
consuming  31
consumer done, notify others producer accquire lock

producer get lock
x>need_max producer wait,and release lock
consumer accquire lock
Consumer get lock
consuming  30
consumer done, notify others
consumer accquire lockproducer accquire lock

Semaphore

(TO DO)

Event

事件对象是实现线程之间安全通通信的一种简单的方法。Event内部管理着一个内部标志,调用者可以用set()clear()方法控制这个标志。其他线程可以使用wait()暂停直到设置这个标志,即线程调用wait()等待,调用的线程被阻塞,直到event被set或timeout,才继续执行。
以车辆等待红绿灯为例。

import threading 
import time

class VehicleThread(threading.Thread):
    def __init__(self,threadName,event):
        threading.Thread.__init__(self,name=threadName)
        self.event = event
    def run(self):
        time.sleep(int(self.getName()[7:]))
        print self.getName(),'arrive,wait for green light'
        self.event.wait()

        print self.getName(),'passed'
green_light_event = threading.Event()
vehicleThreads = []

for i in xrange(10):
    vehicleThreads.append(VehicleThread('vehicle'+str(i),green_light_event))

for t in vehicleThreads:
    t.start()

while threading.activeCount() > 2: #在python中除了主线程还有一个Thread-1,
                                   #IPython运行有5个线程?(Thread-5 Thread-1 MainThread Thread-4 IPythonHistorySavingThread)
    green_light_event.clear()

    time.sleep(3)

    print "green light appera!!! let's GO"
    green_light_event.set()
    time.sleep(1)
#   for t in threading.enumerate():
#         print t.getName()

thread与Queue的联合使用

(TO DO)

参考链接

0

评论 (0)

取消
粤ICP备2021042327号