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,但也有时候会出现错误,解决方法请继续阅 读下文。
要点:

  1. threading.Thread(target=func, args=(10,)):func 为函数名,args 为函数参数(必须以元组的形式传递 );

  2. 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)            

要点:

  1. lock = threading.Lock():创建锁;

  2. 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():条件创造后调用,通知等待池激活所有线程。 

 
原文地址:https://www.cnblogs.com/lixiaomingpython/p/6820900.html