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

关于threading模块:

1、关于 传参问题

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

  如下:

1     for i in xrange(5):
2         threads.append(threading.Thread(target=worker,args=(i,)))

 

2、关于join()阻塞

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

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

 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.004s

这里,由于5个子线程分别刚被t.start()之后,立即把自身阻塞了,所以它们会按序执行,同样,程序sleep了5秒,一定要注意,你这样做,程序的效率并没有提升,仍然需要5秒的时间。所以这样是有问题的,问题出在t.join()代码放的位置,应该再 for t in threads:  t.join(),使得这些线程同时start,然后同时join()。

 

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

worker 0
worker 1
worker 2
worker 3
worker 4
finished: 1.003s

这里,阻塞父线程,父线程会等待子线程结束,才会继续运行打印finished,程序的效率也得到了提升。这相当于上面提到的,先把所有的子线程start了,再join掉。

 

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.001s

说明,主线程一旦结束,会直接把子线程的内存回收,结束整个进程的运行。子进程的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

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-10-01
  • 2021-06-08
  • 2021-09-17
  • 2022-12-23
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-07-15
  • 2022-01-04
  • 2022-12-23
  • 2022-12-23
相关资源
相似解决方案