初识多线程

 1 import threading
 2 import time
 3 def coding():
 4     for i in range(3):
 5         print('输入代码%s' %i)
 6         time.sleep(1)
 7 def drawing():
 8     for i in range(3):
 9         print('开始画画%s'%i)
10         time.sleep(1)
11 def main():
12     t1 = threading.Thread(target=coding)
13     t2 = threading.Thread(target=drawing)
14 
15     t1.start()
16     t2.start()
17 if __name__ == '__main__':
18     main()
19 
20 Output:
21 
22 D:\PyCharm\Anaconda3\python.exe D:/PyCharm/Py_work/test2.py
23 输入代码0
24 开始画画0
25 输入代码1
26 开始画画1
27 输入代码2
28 开始画画2
29 
30 Process finished with exit code 0

还可以通过继承Thread类来创建多线程:

 1 import threading
 2 import time
 3 class codingThread(threading.Thread):
 4     def run(self):
 5         for i in range(3):
 6             print('输入代码%s' %threading.current_thread())
 7             time.sleep(1)
 8 class drawingThread(threading.Thread):
 9     def run(self):
10         for i in range(3):
11             print('开始画画%s'%threading.current_thread())
12             time.sleep(1)
13 def main():
14     t1 = codingThread()
15     t2 = drawingThread()
16 
17     t1.start()
18     t2.start()
19 if __name__ == '__main__':
20     main()
21 
22 Output
23 
24 D:\PyCharm\Anaconda3\python.exe D:/PyCharm/Py_work/test2.py
25 输入代码<codingThread(Thread-1, started 8836)>
26 开始画画<drawingThread(Thread-2, started 9080)>
27 开始画画<drawingThread(Thread-2, started 9080)>
28 输入代码<codingThread(Thread-1, started 8836)>
29 输入代码<codingThread(Thread-1, started 8836)>
30 开始画画<drawingThread(Thread-2, started 9080)>
31 
32 Process finished with exit code 0

多线程共享全局变量的问题:

多线程都是在同一个进程中运行的。因此在进程中的全局变量所有线程都是共享的。这就造成了一个问题,因为线程执行的顺序是无序的,有可能会造成数据错误。

 1 ###当数据量小的时候,可能不会出错,如下:
 2 import threading
 3 value = 0
 4 def add_value():
 5     global value
 6     for i in range(1000):
 7         value += 1
 8     print(value)
 9 def main():
10     for x in range(2):
11         t = threading.Thread(target=add_value)
12         t.start()
13 
14 if __name__ == '__main__':
15     main()
16 
17 Output
18 
19 1000
20 2000

第一个线程先加到1000,第二线程再加到2000,这里看起来没什么问题。但是当数据量大的时候,问题就出现了,如下:

 1 import threading
 2 value = 0
 3 def add_value():
 4     global value
 5     for i in range(1000000):
 6         value += 1
 7     print(value)
 8 def main():
 9     for x in range(2):
10         t = threading.Thread(target=add_value)
11         t.start()
12 
13 if __name__ == '__main__':
14     main()
15 
16 Output
17 
18 1222047
19 1246006

按理说应该先输出1000000,再输出2000000。但是结果却不是,这就是多线程执行顺序无序的后果。举个例子:当value=10的时候,线程1和线程2同时启动,本来value经过两个线程执行后应该是value=12。但是由于两个线程是同时执行,导致value +=1 被同时执行了两次,最后的结果就是value还是等于11.

这时候,就需要一个来消除这种不好的后果。就是如果线程1比线程2线对value先执行的时候,那么就先把value给锁起来,不让其他线程对value进行任何操作,等线程1操作完后再对value 进行解锁,这时候其他线程才可以对value进行操作。(这样看起来,好像和单线程没啥区别~~~~~)。

注意:这个锁一般只用于多个线程修改全局变量的代码段。比如下面代码的7行和8行。

 1 import threading
 2 value = 0
 3 gLock = threading.Lock()
 4 def add_value():
 5     global value
 6     gLock.acquire()  #上锁
 7     for i in range(1000000):
 8         value += 1
 9     gLock.release()  #解锁
10     print(value)
11 def main():
12     for x in range(2):
13         t = threading.Thread(target=add_value)
14         t.start()
15 
16 if __name__ == '__main__':
17     main()
18 
19 Output
20 
21 1000000
22 2000000

 

 

Lock版本生产者和消费者模式

生产者和消费者模式是多线程开发中经常见到的一种模式。生产者的线程专门用来生产一些数据,然后存放到一个中间变量中。消费者再从这个中间的变量中取出来数据进行消费。但是因为要使用中间变量,中间变量经常是一些全局变量,因此余姚使用锁来保证数据完整性。

 1 import threading,random,time
 2 
 3 gMoney = 1000 #此全局变量用来保存生产和消费的钱
 4 gLock = threading.Lock()
 5 gTotalTimes = 10  #此全局变量用来保存需要消费多少次
 6 gtimes = 0   #此全局变量用来保存生产了多少次
 7 
 8 class producer(threading.Thread):  #生产者
 9     def run(self):
10         global gMoney,gtimes
11         while True:
12             money = random.randint(100,1000)
13             gLock.acquire()     #上锁
14             if gtimes >= gTotalTimes:  #如果生产了10次,就退出循环
15                 gLock.release()
16                 break
17             gMoney += money
18             print('%s生产了%d元钱,剩余%d元钱' %(threading.current_thread(),money,gMoney))
19             gtimes += 1
20             gLock.release()    #解锁
21             time.sleep(0.5)
22 
23 class consumer(threading.Thread):  #消费者
24     def run(self):
25         global gMoney
26         while True:
27             money = random.randint(100,1000)
28             gLock.acquire()
29             if gMoney >= money:
30                 gMoney -= money
31                 print('%s消费了%d元钱,剩余%d元钱' % (threading.current_thread(), money, gMoney))
32             else:
33                 if gtimes >= gTotalTimes:
34                     gLock.release()
35                     break
36                 print('%s消费者准备消费%d元钱,剩余%d元钱,余额不足' % (threading.current_thread(), money, gMoney))
37 
38             gLock.release()  #此处的解锁如果放在if代码块里面,可能会造成死锁
39             time.sleep(0.5)
40 
41 def main():
42     for i in range(5):
43         t = consumer(name='消费者线程%d' %i)
44         t.start()
45 
46     for i in range(5):
47         t = producer(name='生产者线程%d' %i)
48         t.start()
49 
50 if __name__ == '__main__':
51     main()

 

Condition版的生产者和消费者模式:

Lock版本的生产者和消费者模式可以正常的运行。但是存在一个不足,在消费者中,总是通过while Ture死循环并且上锁的方式去判断钱够不够。上锁是一个很费CPU资源的行为,因此这种方式不是最好的。

还有一种更好的方式便是使用threading.Condition来实现。threading.Condition可以在没有数据的时候处于阻塞等待状态。一旦有合适的数据了,还可以使用notify相关的函数来同时其他处于等待状态的线程。这样就可以不用做一些无用的上锁和解锁操作,可以提高程序的性能。threading.Condition和threading.Lock部分功能类似,其相关函数介绍如下:

1、acquire:上锁

2、release:解锁

3、wait:将当前线程处于等待状态,并且会释放锁,可以被其他线程使用notify或者notify_all函数唤醒。被唤醒后会继续等待上锁,上锁后继续执行下面的代码。

4、notify:通知某个正在等待的线程。默认是第1个等待的线程。

5、notify_all:通知所有正在等待的线程。notify和notify_all不会释放锁,因此需要在release之前调用

 1 import threading,random,time
 2 
 3 gMoney = 1000 #此全局变量用来保存生产和消费的钱
 4 gCondition = threading.Condition()
 5 gTotalTimes = 10  #此全局变量用来保存需要消费多少次
 6 gtimes = 0   #此全局变量用来保存生产了多少次
 7 
 8 class producer(threading.Thread):  #生产者
 9     def run(self):
10         global gMoney,gtimes
11         while True:
12             money = random.randint(100,1000)
13             gCondition.acquire()     #上锁
14             if gtimes >= gTotalTimes:  #如果生产了10次,就退出循环
15                 gCondition.release()
16                 break
17             gMoney += money
18             print('%s生产了%d元钱,剩余%d元钱' %(threading.current_thread(),money,gMoney))
19             gtimes += 1
20             gCondition.notify_all()
21             gCondition.release()    #解锁
22             time.sleep(0.5)
23 
24 class consumer(threading.Thread):  #消费者
25     def run(self):
26         global gMoney
27         while True:
28             money = random.randint(100,1000)
29             gCondition.acquire()
30             while gMoney < money:
31                 if gtimes >= gTotalTimes:
32                     gCondition.release()
33                     return
34                 else:
35                     print('%s准备消费%d元钱,剩余%d元钱,余额不足' % (threading.current_thread(), money, gMoney))
36                 gCondition.wait()
37             gMoney -= money
38             print('%s消费了%d元钱,剩余%d元钱' %(threading.current_thread(),money,gMoney))
39             gCondition.release()  #此处的解锁如果放在if代码块里面,可能会造成死锁
40             time.sleep(0.5)
41 
42 def main():
43     for i in range(5):
44         t = consumer(name='消费者线程%d' %i)
45         t.start()
46 
47     for i in range(5):
48         t = producer(name='生产者线程%d' %i)
49         t.start()
50 
51 if __name__ == '__main__':
52     main()

 

posted @ 2020-06-07 11:31  牛公的跑奔  阅读(128)  评论(0编辑  收藏  举报
总访问量:AmazingCounters.com