未加星标

再看python多线程------threading模块

字体大小 | |
[开发(python) 所属分类 开发(python) | 发布者 店小二05 | 时间 2016 | 作者 红领巾 ] 0人收藏点击收藏

现在把关于多线程的能想到的需要注意的点记录一下:

关于threading模块:

1、关于传参问题

如果调用的子线程函数需要传参,要在参数后面加一个“ , ”否则会抛参数异常的错误。

如下:

1 for i in xrange(5): 2 threads.append(threading.Thread(target=worker,args=(i,))) 2、关于join()阻塞

join()方法一旦被调用,这个线程就会被阻塞住,等其他线程执行完才执行自身。当我们在主线程A中,创建了n个子线程,这里需要注意,根据需求是应该去 阻塞父线程还是子线程 ,比较下面例子:

---->子线程和父线程都不阻塞

1 # -*- coding:utf-8 -*- 2 import Queue,time,threading 3 start = time.clock() 4 5 def worker(m): 6 print 'worker',m 7 time.sleep(1) 8 return 9 10 if __name__ == "__main__": 11 threads = [] 12 for i in xrange(5): 13 threads.append(threading.Thread(target=worker,args=(i,))) 14 for t in threads: 15 t.start() 16 #t.join() #阻塞子线程 17 18 #t.join() #阻塞父线程 19 20 end = time.clock() 21 print "finished: %.3fs" %(end-start)

得到输入:

worker 0

worker 1

worker 2

worker 3

worker 4

finished: 0.001s

这里,其实父线程已经结束,因为已经打印出了finished:0.001s,但是子线程并没有执行完,sleep 1秒之后,才出现“Process finished with exit code 0”的程序结束标志。

----->同样代码,当阻塞子线程时,输出如下:

worker 0

worker 1

worker 2

worker 3

worker 4

finished: 5.004 s

这里,由于5个子线程都被阻塞了,所以它们会按序执行,同样,程序sleep了5秒,一定要注意,你这样做, 程序的效率并没有提升 ,仍然需要5秒的时间。

-------> 同样代码,当阻塞父线程时,输出如下:

worker 0

worker 1

worker 2

worker 3

worker 4

finished: 1.003 s

这里,阻塞父线程,父线程会等待子线程结束,才会继续运行打印finished,程序的效率也得到了提升。

3、关于setDaemon()方法

setDaemon()方法是设置在子线程中的,当我们在父线程A中创建了n个子线程之后,给我们喜欢的子线程设置setDaemon(True)后,当它们的父线程运行结束之后,不管这些子线程运行结束还是没结束,它会直接结束程序。 这里,还有一个需要注意的,setDaemon()方法必须设置在start()方法之前,否则会抛RuntimeError异常。

还用上面的例子:

1 # -*- coding:utf-8 -*- 2 import Queue,time,threading 3 start = time.clock() 4 5 def worker(m): 6 print 'worker',m 7 time.sleep(1) 8 return 9 10 if __name__ == "__main__": 11 threads = [] 12 for i in xrange(5): 13 threads.append(threading.Thread(target=worker,args=(i,))) 14 for t in threads: 15 t.setDaemon(True) 16 t.start() 17 #t.join() #阻塞子线程 18 19 #t.join() #阻塞父线程 20 21 end = time.clock() 22 print "finished: %.3fs" %(end-start)

这里,没有阻塞父线程,得到的输出如下:

worker 0

worker 1

worker 2

worker 3

worker 4

finished: 0.001 s

说明,主线程一旦结束,会直接把子线程的内存回收,结束整个进程的运行。子进程的sleep 1没有执行就退出了。对于某些辅助子线程的应用场景,这个应该会有用。

4、创建子线程的两种方式

第一种是上面提到的,创建子线程要执行的函数(worker),然后把这个函数传递进threading.Thread的对象中,让它来执行;

第二种是直接从threading.Thread类继承,创建一个新的类,通过重写这个新的类里面的run()方法,实现子线程要执行的内容,例如:

1 # -*- coding:utf-8 -*- 2 __author__ = 'webber' 3 import threading,time 4 5 class Mythread(threading.Thread): 6 7 def __init__(self,m): 8 threading.Thread.__init__(self) 9 self.m = m 10 11 def run(self): 12 print 'worker', self.m 13 time.sleep(1) 14 return 15 16 if __name__ == "__main__": 17 start = time.clock() 18 19 threads = [] 20 for i in xrange(5): 21 threads.append(Mythread(i)) 22 for t in threads: 23 t.start() 24 25 t.join() 26 end = time.clock() 27 print "finished: %.3fs" % (end - start)

输出和上面的主线程阻塞的结果一样

这里要注意一下黄色部分,调用的时候的传参方式。

5、关于锁----> Lock、RLock、Condition方法

之前有提到,由于python理论上是无法实现真正意义上的多线程的,即使你有多个CPU,python的多线程也只能利用一个,那么为了防止在多线程中对共享数据空间的数据修改时发生的尴尬,threading模块继承了thread模块的Lock方法,这是最简单的锁,实现也比较简单,只需要在子线程中修改数据前后分别加上锁和释放锁即可。

就是以下三句话:

a、主函数中创建一个锁的对象: 例如: lock = threading.Lock() #返回一个新的Lock对象,创建一把锁。

b、在子线程需要对数据进行修改之前,lock.acquire() #获取这把锁

c、在子线程对数据进行修改之后, lock.acquire() #释放这把锁

下面有个代码应用小例子:

1 # -*- coding:utf-8 -*- 2 __author__ = 'webber' 3 import threading, time, random 4 5 dish = 0 6 lock = threading.Lock() 7 8 9 def producerFunction(): 10 '''如果投的筛子比0.5大,则向盘子中增加一个苹果''' 11 global lock, dish 12 while dish < 10: 13 if (random.random() > 0.5): 14 lock.acquire() 15 dish += 1 16 print('生产者增加了一个苹果,现在有%d个苹果' % (dish,)) 17 lock.release() 18 time.sleep(random.random() * 3) 19 20 21 def consumerFunction(): 22 '''如果投的筛子比0.5小,则从盘子中取一个苹果''' 23 global lock, dish 24 while dish > 0: 25 if (random.random() < 0.5): 26 lock.acquire() 27 dish -= 1 28 print('消费者拿走一个苹果现,现在有%d个苹果' % (dish,)) 29 lock.release() 30 time.sleep(random.random() * 3) 31 32 33 def begin(): 34 ident1 = threading.Thread(target=producerFunction()) 35 ident2 = threading.Thread(target=consumerFunction()) 36 ident1.start() 37 ident2.start() 38 39 40 if __name__ == '__main__': 41 begin() View Code

其次,threading模块提出了一个更高级的锁RLock,它的出现是为了解决Lock可能会出现的死锁问题,即:当由于疏忽时,可能会出现一个子线程内同一把锁对象连续acquire()两次,那么由于第一次的acquire没有release,那么第二次的acquire请求会把该子线程挂起,导致lock对象永远不会release,造成死锁。而RLock对象允许一个线程多次对其进行acquire操作,在其内部通过counter变量维护着线程acquire的次数,而每一次的acquire操作必须有一个release操作与之对应,在所有的release操作完成之后,别的线程才能申请该RLock对象。使用上暂时我就把它当成Lock方法试了试,通过。~~~

最后,threading模块提供了更高级的封装,算是一种高级的多线程间同步方式,包括threading.Event和threading.Condition,其中,threading.Event为简单的同步方式,一个进程标记为event,其他的进程就需要等待,用到下面几种方法:

Event.wait([timeout]) 阻塞线程,直到Event对象内部标识位被设置为True或超时(如果提供了参数timeout) Event.set() 将标识号设为True Event.clear() 设为标识符False threading.Condition 可以把Condition理解为更高级的锁,它提供了比RLock更高级的功能,允许我们能够控制复杂的线程同步问题,它在内部维护了一个锁对象(默认为RLock),可以 在创建Condition对象的时候把锁对象作为参数传入。 Condition也提供了acquire和release方法,它的特色在于内部的 wait和notify机制 ,具体可看threading模块, 下面的方法只有在对象获取到锁之后才能调用,否则,将会抛RuntimeError异常 。
Condition.wait([timeout]): wait方法释放内部所占用的琐,同时线程被挂起,直至接收到通知被唤醒或超时(如果提供了timeout参数的话)。当线程被唤醒并重新占有琐的时候,程序才会继续执行下去。 Condition.notify() 唤醒一个挂起的线程(如果存在挂起的线程)。注意:notify()方法不会释放所占用的琐。 Condition.notify_all() 唤醒所有挂起的线程(如果存在挂起的线程)。注意:这些方法不会释放所占用的琐。

本文开发(python)相关术语:python基础教程 python多线程 web开发工程师 软件开发工程师 软件开发流程

主题: CPU数据苹果求是其实需求消费变量
分页:12
转载请注明
本文标题:再看python多线程------threading模块
本站链接:http://www.codesec.net/view/484448.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 开发(python) | 评论(0) | 阅读(28)