Python学习总结笔记(3)--多线程与线程同步
2016-11-19 14:41
423 查看
多线程与线程同步网上讲的很多了,这里就简单总结下。
很多地方都讲了Python的多线程实际上是“假的”,原因就是Python的底层实现有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。
所以在Python中,可以使用多线程,但不要指望能有效利用多核。
需要重载run方法即可。下面是一个具体的例子:
子线程对Num进行了10次自增操作,所以最终Num的值为10。
运行结果如下:
结果显示,最终Num的值为19,大于了10。这是因为没有做线程同步造成的:
上面的代码在某一时刻有一个子线程进行了Num<10判断,进入了if判断下的语句,但是没有进行自增运算,在这个时间空隙中,可能还有多个子线程也进入了if下的语句,从而导致最终Num的值大于了10。
Python中提供了“锁”的对象了实现简单的线程同步。建议大家使用RLock这个模块(原因见http://blog.csdn.net/cnmilan/article/details/8849895),修改过的代码如下:
运行结果如下:
运行结果:
实现了条件同步。但是上面的实现过程中,当不符合条件时,子线程会进入下一次循环,试想下,如果是10个自增线程,1个自减线程,那么自增线程将大量陷入“无效”循环中,不断lock.acquire和lock.release,我们能不能让子线程不符合条件时进行wait,然后当条件符合时,再唤醒该子线程继续执行呢?可以利用Python的Condition来实现:
运行结果:
需要注意的是,Condition适合用于不同线程间的同步问题,即用notify“唤醒”的是其他线程,而不是本线程。
还有利用Queue模块实现FIFO同步以及其它一些同步的模块等等,网上例子很多了,这里就不再赘述了。
很多地方都讲了Python的多线程实际上是“假的”,原因就是Python的底层实现有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。
所以在Python中,可以使用多线程,但不要指望能有效利用多核。
0x01 线程处理扩展类
Python中使用线程有两个模块,低级的thread和封装thread的高级threading,建议用threading,还可以自己再扩展threading。简单模型如下:#/usr/bin/env python #coding:utf-8 __author__ = 'kikay' import threading class ThreadEx(threading.Thread): def __init__(self): super(ThreadEx,self).__init__() def run(self): pass
需要重载run方法即可。下面是一个具体的例子:
# /usr/bin/env python # coding:utf-8 __author__ = 'kikay' import threading import random import time # 静态变量 Num = 0 class ThreadEx(threading.Thread): def __init__(self,name): super(ThreadEx, self).__init__() self.__name=name def run(self): time.sleep(1) global Num print 'thread({0})is running...'.format(self.__name) #运行10次 for i in range(10): Num+=1 if __name__ == '__main__': t = ThreadEx('t1') t.start() print 'main is running ...' t.join() print 'Num is ',Num
子线程对Num进行了10次自增操作,所以最终Num的值为10。
0x02 线程同步
现在多个子线程同时对Num进行自增操作,但是保证Num的值不大于10:# /usr/bin/env python # coding:utf-8 __author__ = 'kikay' import threading import random import time # 静态变量 Num = 0 class ThreadEx(threading.Thread): def __init__(self,name): super(ThreadEx, self).__init__() self.__name=name def run(self): #time.sleep(1) global Num print 'thread({0})is running...'.format(self.__name) #运行10次 for i in range(10): if Num<10: time.sleep(0.1) Num=Num+1 print "thread({0})'s Num is {1}".format(self.__name,Num) if __name__ == '__main__': threadNum=10 threads=[] for i in range(threadNum): t = ThreadEx('t{0}'.format(i)) t.daemon=False threads.append(t) for i in range(threadNum): threads[i].start() print 'main is running ...' for i in range(threadNum): threads[i].join() print 'Num is ',Num
运行结果如下:
thread(t0)is running... thread(t1)is running... thread(t2)is running... thread(t3)is running... thread(t4)is running... thread(t5)is running... thread(t6)is running... thread(t7)is running... thread(t8)is running... thread(t9)is running... main is running ... thread(t0)'s Num is 1 thread(t2)'s Num is 3thread(t1)'s Num is 3 thread(t3)'s Num is 4thread(t4)'s Num is 5 thread(t5)'s Num is 6 thread(t6)'s Num is 7 thread(t7)'s Num is 8 thread(t8)'s Num is 9 thread(t9)'s Num is 10 thread(t0)'s Num is 11 thread(t1)'s Num is 12 thread(t2)'s Num is 13 thread(t4)'s Num is 14 thread(t5)'s Num is 15 thread(t3)'s Num is 16 thread(t6)'s Num is 17 thread(t7)'s Num is 18 thread(t8)'s Num is 19 Num is 19
结果显示,最终Num的值为19,大于了10。这是因为没有做线程同步造成的:
if Num<10: time.sleep(0.1) Num=Num+1 print "thread({0})'s Num is {1}".format(self.__name,Num)
上面的代码在某一时刻有一个子线程进行了Num<10判断,进入了if判断下的语句,但是没有进行自增运算,在这个时间空隙中,可能还有多个子线程也进入了if下的语句,从而导致最终Num的值大于了10。
Python中提供了“锁”的对象了实现简单的线程同步。建议大家使用RLock这个模块(原因见http://blog.csdn.net/cnmilan/article/details/8849895),修改过的代码如下:
# /usr/bin/env python
# coding:utf-8
__author__ = 'kikay'
import threading
import random
import time
# 静态变量
Num = 0
lock=threading.RLock()
class ThreadEx(threading.Thread):
def __init__(self,name):
super(ThreadEx, self).__init__()
self.__name=name
def run(self):
#time.sleep(1)
global Num
global lock
print 'thread({0})is running...'.format(self.__name)
#运行10次
for i in range(10):
lock.acquire()
try:
if Num<10: time.sleep(0.1) Num=Num+1 print "thread({0})'s Num is {1}".format(self.__name,Num)
finally:
lock.release()
if __name__ == '__main__':
threadNum=10
threads=[]
for i in range(threadNum):
t = ThreadEx('t{0}'.format(i))
t.daemon=False
threads.append(t)
for i in range(threadNum):
threads[i].start()
print 'main is running ...'
for i in range(threadNum):
threads[i].join()
print 'Num is ',Num
运行结果如下:
thread(t0)is running... thread(t1)is running... thread(t2)is running... thread(t3)is running... thread(t4)is running... thread(t5)is running... thread(t6)is running... thread(t7)is running... thread(t8)is running... thread(t9)is running... main is running ... thread(t0)'s Num is 1 thread(t1)'s Num is 2 thread(t2)'s Num is 3 thread(t3)'s Num is 4 thread(t4)'s Num is 5 thread(t5)'s Num is 6 thread(t6)'s Num is 7 thread(t7)'s Num is 8 thread(t8)'s Num is 9 thread(t9)'s Num is 10 Num is 10
0x03 条件同步
上面的“锁”实现了基本的同步,如果现在有5个自增线程和5个自减线程同时运行(无限循环),保证Num的值不能小于0,不能大于10,继续用上面的RLock,修改后的代码如下:# /usr/bin/env python # coding:utf-8 __author__ = 'kikay' import threading import random import time # 静态变量 Num = 0 lock=threading.RLock() #自增线程类 class ThreadAddEx(threading.Thread): def __init__(self,name): super(ThreadAddEx, self).__init__() self.__name=name self.__working=True def run(self): time.sleep(0.1) global Num global lock #print 'threadAdd({0})is running...'.format(self.__name) while self.__working: lock.acquire() try: if Num<10: time.sleep(0.1) Num=Num+1 print "threadAdd({0})'s Num is {1}".format(self.__name,Num) finally: lock.release() def stop(self): self.__working=False #自减线程类 class ThreadSubEx(threading.Thread): def __init__(self,name): super(ThreadSubEx, self).__init__() self.__name=name self.__working=True def run(self): time.sleep(0.1) global Num global lock #print 'threadSub({0})is running...'.format(self.__name) while self.__working: lock.acquire() try: if Num>0: time.sleep(0.1) Num=Num-1 print "threadSub({0})'s Num is {1}".format(self.__name,Num) finally: lock.release() def stop(self): self.__working=False if __name__ == '__main__': threadNum=5 threads=[] for i in range(threadNum): t = ThreadAddEx('t{0}'.format(i)) t.daemon=False threads.append(t) for i in range(threadNum): t = ThreadSubEx('t{0}'.format(i)) t.daemon=False threads.append(t) for i in range(len(threads)): threads[i].start() print 'main is running ...' #运行1s time.sleep(1) #停止 for i in range(len(threads)): threads[i].stop() for i in range(len(threads)): threads[i].join() print 'Num is ',Num
运行结果:
main is running ... threadAdd(t0)'s Num is 1 threadAdd(t3)'s Num is 2 threadSub(t0)'s Num is 1 threadAdd(t4)'s Num is 2 threadAdd(t2)'s Num is 3 threadSub(t1)'s Num is 2 threadAdd(t1)'s Num is 3 threadSub(t3)'s Num is 2 threadSub(t2)'s Num is 1 threadSub(t4)'s Num is 0 threadAdd(t0)'s Num is 1 threadAdd(t3)'s Num is 2 threadSub(t0)'s Num is 1 threadAdd(t4)'s Num is 2 threadAdd(t2)'s Num is 3 threadSub(t1)'s Num is 2 threadAdd(t1)'s Num is 3 threadSub(t3)'s Num is 2 Num is 2
实现了条件同步。但是上面的实现过程中,当不符合条件时,子线程会进入下一次循环,试想下,如果是10个自增线程,1个自减线程,那么自增线程将大量陷入“无效”循环中,不断lock.acquire和lock.release,我们能不能让子线程不符合条件时进行wait,然后当条件符合时,再唤醒该子线程继续执行呢?可以利用Python的Condition来实现:
# /usr/bin/env python # coding:utf-8 __author__ = 'kikay' import threading import random import time # 静态变量 Num = 0 # lock=threading.RLock() con = threading.Condition() # 自增线程类 class ThreadAddEx(threading.Thread): def __init__(self, name): super(ThreadAddEx, self).__init__() self.__name = name self.__working = True def run(self): time.sleep(0.1) global Num # global lock global con # print 'threadAdd({0})is running...'.format(self.__name) while self.__working: con.acquire() try: time.sleep(0.1) if Num < 5: Num = Num + 1 print "threadAdd({0})'s Num is {1}".format(self.__name, Num) # 肯定大于0,唤醒减线程 con.notify() # 等待 else: print 'threadAdd waiting ...' con.wait() finally: con.release() def stop(self): self.__working = False # 自减线程类 class ThreadSubEx(threading.Thread): def __init__(self, name): super(ThreadSubEx, self).__init__() self.__name = name self.__working = True def run(self): time.sleep(0.1) global Num # global lock global con # print 'threadSub({0})is running...'.format(self.__name) while self.__working: con.acquire() try: time.sleep(0.1) if Num > 0: Num = Num - 1 print "threadSub({0})'s Num is {1}".format(self.__name, Num) # 肯定小于10了,唤醒自增线程 con.notify() # 等待 else: print 'threadSub waiting ...' con.wait() finally: con.release() def stop(self): self.__working = False if __name__ == '__main__': threadNum = 10 threads = [] for i in range(threadNum): t = ThreadAddEx('t{0}'.format(i)) t.daemon = False threads.append(t) for i in range(threadNum): t = ThreadSubEx('t{0}'.format(i)) t.daemon = False threads.append(t) for i in range(len(threads)): threads[i].start() print 'main is running ...' # 运行5s time.sleep(5) # 停止 for i in range(len(threads)): threads[i].stop() for i in range(len(threads)): threads[i].join() print 'Num is ', Num
运行结果:
main is running ... threadAdd(t6)'s Num is 1 threadAdd(t4)'s Num is 2 threadAdd(t2)'s Num is 3 threadAdd(t0)'s Num is 4 threadAdd(t5)'s Num is 5 threadAdd waiting ... threadAdd waiting ... threadSub(t0)'s Num is 4 threadSub(t2)'s Num is 3 threadSub(t4)'s Num is 2 threadSub(t6)'s Num is 1 threadSub(t8)'s Num is 0 threadAdd(t9)'s Num is 1 threadSub(t1)'s Num is 0 threadSub waiting ... threadSub waiting ... threadSub waiting ... threadSub waiting ... threadAdd(t7)'s Num is 1 threadAdd(t8)'s Num is 2 threadAdd(t6)'s Num is 3 threadAdd(t4)'s Num is 4 threadAdd(t2)'s Num is 5 threadAdd waiting ... threadAdd waiting ... threadSub(t0)'s Num is 4 threadSub(t2)'s Num is 3 threadSub(t4)'s Num is 2 threadSub(t6)'s Num is 1 threadSub(t8)'s Num is 0 threadAdd(t9)'s Num is 1 threadSub(t1)'s Num is 0 threadAdd(t7)'s Num is 1 threadAdd(t8)'s Num is 2 threadAdd(t6)'s Num is 3 threadAdd(t4)'s Num is 4 threadAdd(t2)'s Num is 5 threadAdd waiting ... threadSub(t0)'s Num is 4 threadSub(t2)'s Num is 3 threadAdd(t1)'s Num is 4 threadSub(t4)'s Num is 3 threadSub(t6)'s Num is 2 threadSub(t8)'s Num is 1 threadAdd(t9)'s Num is 2 threadSub(t1)'s Num is 1 threadSub(t3)'s Num is 0 threadSub waiting ... threadAdd(t7)'s Num is 1 threadAdd(t8)'s Num is 2 threadSub(t7)'s Num is 1 threadAdd(t6)'s Num is 2 threadSub(t9)'s Num is 1 threadAdd(t4)'s Num is 2 threadAdd(t2)'s Num is 3 threadAdd(t0)'s Num is 4 threadSub(t0)'s Num is 3 threadSub(t2)'s Num is 2 threadAdd(t1)'s Num is 3 threadAdd(t5)'s Num is 4 threadSub(t4)'s Num is 3 threadSub(t6)'s Num is 2 threadSub(t8)'s Num is 1 threadAdd(t9)'s Num is 2 threadSub(t1)'s Num is 1 threadSub(t3)'s Num is 0 Num is 0
需要注意的是,Condition适合用于不同线程间的同步问题,即用notify“唤醒”的是其他线程,而不是本线程。
还有利用Queue模块实现FIFO同步以及其它一些同步的模块等等,网上例子很多了,这里就不再赘述了。
相关文章推荐
- 多线程开发学习笔记之线程同步——事件
- Python学习笔记总结(三):类
- Java多线程学习总结--线程同步(2)
- python学习笔记之多线程
- [简明python教程]学习笔记之总结篇
- .Net学习难点讨论系列14 – 多线程下的进程同步(线程同步问题总结篇)
- 多线程开发学习笔记之线程同步——临界区
- Python学习笔记之数据类型总结
- Python学习笔记—PyQuery库的使用总结
- Java学习笔记总结篇之集合类和多线程
- Python学习笔记总结(二):函数和模块
- python学习笔记17-常用函数总结整理
- 多线程开发学习笔记之线程同步——互斥量
- linux多线程学习笔记四---线程同步之互斥锁、读写锁和条件变量
- python学习笔记五(python多线程)
- 黑马程序员java学习笔记之四(java多线程总结)
- python学习笔记11-异常总结
- python学习笔记18-重点和忘记知识点总结
- python基础教程_学习笔记8:序列_练习与总结_1
- python学习笔记(1)--《python基础教程》第1章内容总结