threading 学习笔记
一、线程基础
1、线程状态
2、线程互斥锁同步控制
多个线程需要修改同一共享数据时,需要进行同步控制,引入了锁的概念。
a、同一时间可能有多个线程在锁定池中,它们处于同步阻塞状态竞争锁定;
b、同一时间只能有一个线程获得锁定处于运行状态。
3、条件变量(线程通信)
有的线程需要预备条件才能干活。
二、threading:线程创建、启动、睡眠、退出
1、方法一:将要执行的函数作为参数传递给 threading.Thread()
1 # -*- coding:utf-8 -*- 2 import threading, time 3 def func(n): 4 global count 5 time.sleep(0.1) 6 for i in range(n): 7 count += 1 8 if __name__==’__main__’: 9 count = 0 10 threads = [] 11 for i in range(5): 12 threads.append(threading.Thread(target=func, args=(1000,))) 13 for t in threads: 14 t.start() 15 time.sleep(5) 16 print(‘count:’,count)
以上例子创建了 5 个线程去执行 func 函数。获得的结果可能是 5000,但也有时候会出现错误,解决方法请继续阅 读下文。
要点:
-
threading.Thread(target=func, args=(10,)):func 为函数名,args 为函数参数(必须以元组的形式传递 );
-
t.start(): 启动函数,等待操作系统调度;
c. 函数运行结束,线程也就结束;
d. time.sleep(): 线程进入睡眠,处于 IO 阻塞状态。
1、方法二:继承 threading.Thread()类,并重写 run()
1 # -*- coding:utf-8 -*- 2 import threading, time 3 class myThread(threading.Thread): 4 def __init__(self, n): 5 threading.Thread.__init__(self) 6 self.myThread_n = n 7 def run(self): 8 global count 9 for i in range(self.myThread_n): 10 count += 1 11 if __name__==’__main__’: 12 count = 0 13 threads = [] 14 for i in range(5): 15 threads.append(myThread(1000)) 16 for t in threads: 17 t.start() 18 time.sleep(5) 19 print(‘count:’,count)
要点:
a. threading.Thread.__init__(self):回调父类方法。如果你重写了__init__()方法,这一步是必须的,否则出错。
三、threading:线程同步锁互斥控制
因为线程是乱序执行的,在某种情况下上面的两个例子,输出的结果可能不是预期的值。
我将第 2 例的线程类修改下,让问题更加突出,然后你每次运行的时候再把线程数也修改下,
并计算出预期结果和运行 结果对比。一定要多试几次哦。
1 class myThread(threading.Thread): 2 def __init__(self, n): 3 threading.Thread.__init__(self) 4 self.myThread_n = n 5 def run(self): 6 global count 7 for i in range(self.myThread_n): 8 __Temp = count time.sleep(0.0001) 9 count = __Temp + 1
是不是输出的结果和预期结果不一致啊,呵呵!因为多个线程都在同时操作同一个共享资源
,所以造成资源破坏。不过 我们可以通过同步锁来解决这种问题:
1 # -*- coding:utf-8 -*- 2 import threading, time 3 class myThread(threading.Thread): 4 def __init__(self, n): 5 threading.Thread.__init__(self) 6 self.myThread_n = n 7 def run(self): 8 global count 9 for i in range(self.myThread_n): 10 if lock.acquire(): 11 __Temp = count 12 time.sleep(0.0001) 13 count = __Temp + 1 14 lock.release() 15 if __name__==’__main__’: 16 count = 0 17 lock = threading.Lock() #同步锁 18 threads = [] 19 for i in range(5): 20 21 threads.append(myThread(1000)) for t in threads: 22 t.start() time.sleep(5) 23 print(‘count:’,count)
要点:
-
lock = threading.Lock():创建锁;
-
lock.acquire([timeount]):请求锁定,如果设定了 timeout,则在超时后通过返回值可以判断是否得到了锁 ,
从而可以进行一些其他的处理 ; c. lock.release():释放锁定。
四、threading:线程死锁和递归锁
1、死锁
a. 在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,
就会造成死锁 , 因为系统判断这部分资源都正在使用 ,所有这两个线程在无外力作用下将一直
等待下去 。下面是一个死锁的例 子:
1 # -*- coding:utf-8 -*- 2 import threading, time 3 class myThread(threading.Thread): 4 def doA(self): 5 if lockA.acquire(): 6 print(self.name,”got lockA.”) time.sleep(0.0001) 7 if lockB.acquire(): 8 print(self.name,”got lockB”) 9 lockB.release() 10 lockA.release() 11 def doB(self): 12 if lockB.acquire(): 13 print(self.name,”got lockB.”) 14 time.sleep(0.0001) 15 if lockA.acquire(): 16 print(self.name,”got lockA”) 17 lockA.release() 18 lockB.release() 19 def run(self): 20 self.doA() 21 self.doB() 22 if __name__==’__main__’: 23 lockA = threading.Lock() 24 lockB = threading.Lock() 25 threads = [] 26 for i in range(5): 27 threads.append(myThread()) 28 for t in threads: 29 t.start() 30 for t in threads: 31 t.join() #等待线程结束,后面再讲。
b. 当一个线程已经获得了锁,再此请求锁也会出现死锁,请看下面的例子:
1 # -*- coding:utf-8 -*- 2 import threading, time 3 import random 4 class myThread(threading.Thread): 5 def toHex(self): 6 global L 7 if lock.acquire(1): 8 for i in range(len(L)): 9 if type(L[i]) is int: L[i]=”{:X}”.format(L[i]) 10 lock.release() 11 else: 12 print(self.name,“错误,系统忙”) 13 def run(self): 14 global L 15 if lock.acquire(1): 16 L.append(random.randint(0, 15)) 17 self.toHex() 18 lock.release() 19 else: 20 print(self.name,“错误,系统忙”) 21 if __name__==’__main__’: 22 L = [] 23 lock = threading.Lock() 24 threads = [] 25 for i in range(5): 26 threads.append(myThread()) 27 for t in threads: 28 t.start() 29 for t in threads: 30 t.join() #等待线程结束,后面再讲。
2、递归锁(也称可重入锁)
上一例子的死锁,我们可以用递归锁解决:
1 #lock = threading.Lock() 注释掉此行,并加入下行。 2 lock = threading.RLock()
为了支持在同一线程中多次请求同一资源 ,python 提供了“可重入锁”:threading.RLock。RLock 内部维护着一个 Lock 和一个 counter 变量,counter 记录了 acquire 的次数,从而使得资源可以被多次 require。直到一个线程所有的 acquire 都 被 release,其他的线程才能获得资源。
递归锁一般应用于复杂的逻辑。
五、threading:条件变量同步
有一类线程需要满足条件之后才能够继续执行, Python 提供了 threading.Condition 对象用于条件变量线程的支持,它除 了能提供 RLock()或 Lock()的方法外,还提供了 wait()、notify()、notifyAll()方法。
lock_con = threading.Condition([Lock/Rlock]) :锁是可选选项,不传人锁,对象自动创建一个 RLock()。 wait():条件不满足时调用,线程会释放锁并进入等待阻塞; notify():条件创造后调用,通知等待池激活一个线程;
#lock = threading.Lock() 注释掉此行,并加入下行。 lock = threading.RLock()
notifyAll():条件创造后调用,通知等待池激活所有线程。