?? 引言/动机
?? 线程和进程
?? 线程和 Python
?? thread 模块
?? threading 模块
?? 生产者-消费者问题和 Queue 模块
?? 相关模块
18.1 引言/动机
18.2 线程和进程
什么是进程?
计算机程序只不过是磁盘中可执行的,二进制(或其它类型)的数据。它们只有在被读取到内
存中,被操作系统调用的时候才开始它们的生命期。进程(有时被称为重量级进程)是程序的一次
执行。每个进程都有自己的地址空间,内存,数据栈以及其它记录其运行轨迹的辅助数据。操作系
统管理在其上运行的所有进程,并为这些进程公平地分配时间。进程也可以通过fork 和spawn 操作
来完成其它的任务。不过各个进程有自己的内存空间,数据栈等,所以只能使用进程间通讯(IPC),
而不能直接共享信息。
什么是线程?
线程(有时被称为轻量级进程)跟进程有些相似,不同的是,所有的线程运行在同一个进程中, 共享相同的运行环境。
线程有开始,顺序执行和结束三部分。它有一个自己的指令指针,记录自己运行到什么地方。
线程的运行可能被抢占(中断),或暂时的被挂起(也叫睡眠),让其它的线程运行,这叫做让步。
一个进程中的各个线程之间共享同一片数据空间,所以线程之间可以比进程之间更方便地共享数据
以及相互通讯。线程一般都是并发执行的,正是由于这种并行和数据共享的机制使得多个任务的合
作变为可能。实际上,在单CPU 的系统中,真正的并发是不可能的,每个线程会被安排成每次只运
行一小会,然后就把CPU 让出来,让其它的线程去运行。在进程的整个运行过程中,每个线程都只
做自己的事,在需要的时候跟其它的线程共享运行的结果。
如果多个线程共同访问同一片数据,则由于数据访 问的顺序不一样,有可能导致数据结果的不一致
的问题。这叫做竞态条件(race condition)。幸运的是,大多数线程库都带有一系列的同步原语,来
控制线程的执行和数据的访问。
18.3 Python、线程和全局解释器锁
全局解释器锁(GIL)
Python 代码的执行由Python 虚拟机(也叫解释器主循环)来控制。Python 在设计之初就考虑到
要在主循环中,同时只有一个线程在执行,就像单CPU 的系统中运行多个进程那样,内存中可以存
放多个程序,但任意时刻,只有一个程序在CPU 中运行。同样地,虽然Python 解释器中可以“运行”
多个线程,但在任意时刻,只有一个线程在解释器中运行。
对Python 虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个
线程在运行。在多线程环境中,Python 虚拟机按以下方式执行:
在调用外部代码(如C/C++扩展函数)的时候,GIL 将会被锁定,直到这个函数结束为止(由于
在这期间没有Python 的字节码被运行,所以不会做线程切换)。编写扩展的程序员可以主动解锁GIL。
不过,Python 的开发人员则不用担心在这些情况下你的Python 代码会被锁住。
退出线程
当一个线程结束计算,它就退出了。线程可以调用thread.exit()之类的退出函数,也可以使用
Python 退出进程的标准方法,如sys.exit()或抛出一个SystemExit 异常等。不过,你不可以直接
“杀掉”("kill")一个线程。
在Python 中使用线程
在Win32 和Linux, Solaris, MacOS, *BSD 等大多数类Unix 系统上运行时,Python 支持多线程编程。
Python 使用POSIX 兼容的线程,即pthreads。
默认情况下,从源代码编译的(2.0 及以上版本的)Python 以及Win32 的安装包里,线程支持是打开的。
没有线程支持的情况
1 #!/usr/bin/env python 2 3 from time import sleep, ctime 4 5 def loop0(): 6 print ‘start loop 0 at:‘, ctime() 7 sleep(4) 8 print ‘loop 0 done at:‘, ctime() 9 10 def loop1(): 11 print ‘start loop 1 at:‘, ctime() 12 sleep(2) 13 print ‘loop 1 done at:‘, ctime() 14 15 def main(): 16 print ‘starting at:‘, ctime() 17 loop0() 18 loop1() 19 print ‘all DONE at:‘, ctime() 20 21 if __name__ == ‘__main__‘: 22 main()
onethr.py
Python 的threading 模块
Python 提供了几个用于多线程编程的模块,包括thread, threading 和Queue 等。thread 和
threading 模块允许程序员创建和管理线程。thread 模块提供了基本的线程和锁的支持,而threading
提供了更高级别,功能更强的线程管理的功能。Queue 模块允许用户创建一个可以用于多个线程之间
共享数据的队列数据结构。我们将分别介绍这几个模块,并给出一些例子和中等大小的应用。
核心提示:避免使用thread 模块
首先,更高级别的threading 模块更为先进,对线程的支持更为完善,而且使用thread 模块里的属性有可能会与threading 出现冲突。
其次, 低级别的thread 模块的同步原语很少(实际上只有一个),而threading 模块则有很多。
最后,对于你的进程什么时候应该结束完全没有控制,当主线程结束时,所有的线程都会被强制结束掉,没有警告也不会有正常的清除工作。
只建议那些有经验的专家在想访问线程的底层结构的时候,才使用thread 模块。
18.4 thread 模块
使用thread 模块 (mtsleep1.py)
1 #!/usr/bin/env python 2 3 import thread 4 from time import sleep, ctime 5 6 def loop0(): 7 print ‘start loop 0 at:‘, ctime() 8 sleep(4) 9 print ‘loop 0 done at:‘, ctime() 10 11 def loop1(): 12 print ‘start loop 1 at:‘, ctime() 13 sleep(2) 14 print ‘loop 1 done at:‘, ctime() 15 16 def main(): 17 print ‘starting at:‘, ctime() 18 thread.start_new_thread(loop0, ()) 19 thread.start_new_thread(loop1, ()) 20 sleep(6) 21 print ‘all DONE at:‘, ctime() 22 23 if __name__ == ‘__main__‘: 24 main()
mtsleep1.py
使用线程和锁 (mtsleep2.py)
1 #!/usr/bin/env python 2 3 import thread 4 from time import sleep, ctime 5 6 loops = [4,2] 7 8 def loop(nloop, nsec, lock): 9 print ‘start loop‘, nloop, ‘at:‘, ctime() 10 sleep(nsec) 11 print ‘loop‘, nloop, ‘done at:‘, ctime() 12 lock.release() 13 14 def main(): 15 print ‘starting at:‘, ctime() 16 locks = [] 17 nloops = range(len(loops)) 18 19 for i in nloops: 20 lock = thread.allocate_lock() 21 lock.acquire() 22 locks.append(lock) 23 24 for i in nloops: 25 thread.start_new_thread(loop, (i, loops[i], locks[i])) 26 27 for i in nloops: 28 while locks[i].locked(): pass 29 30 print ‘all DONE at:‘, ctime() 31 32 if __name__ == ‘__main__‘: 33 main()
mtsleep2.py
18.5 threading 模块
Thread 类
threading 的Thread 类是你主要的运行对象。它有很多thread 模块里没有的函数。
用Thread 类,你可以用多种方法来创建线程。
创建一个Thread 的实例,传给它一个函数
1 #!/usr/bin/env python 2 3 import threading 4 from time import sleep, ctime 5 6 loops = [4,2] 7 8 def loop(nloop, nsec): 9 print ‘start loop‘, nloop, ‘at:‘, ctime() 10 sleep(nsec) 11 print ‘loop‘, nloop, ‘done at:‘, ctime() 12 13 def main(): 14 print ‘starting at:‘, ctime() 15 threads = [] 16 nloops = range(len(loops)) 17 18 for i in nloops: 19 t = threading.Thread(target=loop,20 args=(i, loops[i])) 21 threads.append(t) 22 23 for i in nloops: # start threads 24 threads[i].start() 25 26 for i in nloops: 27 threads[i].join() # threads to finish 28 29 print ‘all DONE at:‘, ctime() 30 31 if __name__ == ‘__main__‘: 32 main()
mtsleep3.py
创建一个Thread 的实例,传给它一个可调用的类对象
从Thread 派生出一个子类,创建一个这个子类的实例
1 #!user/env/bin python 2 3 import threading 4 from time import sleep, ctime 5 6 loops = [4,2] 7 8 class Thread4(threading.Thread): 9 def __init__(self, nloop, nsec, 10 group=None, target=None, name=None,11 args=(), kwargs=None, verbose=None): 12 self.nloop = nloop 13 self.nsec = nsec 14 threading.Thread.__init__(self) 15 def run(self): 16 print ‘start loop‘, self.nloop, ‘at:‘, ctime() 17 sleep(self.nsec) 18 print ‘loop‘, self.nloop, ‘done at:‘, ctime() 19 20 def main(): 21 print ‘starting at:‘, ctime() 22 threads = list() 23 nloops = range(len(loops)) 24 25 for i in nloops: 26 t = Thread4(i, loops[i]) 27 threads.append(t) 28 29 for i in nloops: 30 threads[i].start() 31 32 for i in nloops: 33 threads[i].join() 34 35 print ‘all DONE at:‘, ctime() 36 37 if __name__ == ‘__main__‘: 38 main()
mtsleep5.py
1 #!usr/bin/env python 2 3 from myThread import MyThread 4 from time import time 5 6 def fabonacci(n): 7 if n <= 2: 8 return 1 9 if n > 2: 10 return fabonacci(n-1) + fabonacci(n-2) 11 12 def factorial(n): 13 if n <= 1: 14 return 1 15 if n > 1: 16 return factorial(n-1) * n 17 18 def count(n): 19 sum = 0 20 for i in range(n+1): 21 sum += i 22 return sum 23 24 def main(): 25 t1 = time() 26 threads = list() 27 loops = [32,10,100000] 28 29 threads.append(MyThread(fabonacci, (loops[0],))) 30 threads.append(MyThread(factorial, (loops[1],))) 31 threads.append(MyThread(count, (loops[2],))) 32 33 for t in threads: 34 t.start() 35 36 for t in threads: 37 t.join() 38 39 for t in threads: 40 print t.func.__name__, ‘(%d)=‘ % t.args[0],41 t.getResult(), ‘time:%f‘ % t.getts() 42 t2 = time() 43 print ‘All time:%f‘ % (t2-t1) 44 45 t1 = time() 46 fabonacci(loops[0]) 47 factorial(loops[1]) 48 count(loops[2]) 49 t2 = time() 50 print ‘%f‘ % (t2-t1) 51 52 53 if __name__ == ‘__main__‘: 54 main() 55
mtfabonacci.py
1 #!usr/bin/env python 2 3 import threading 4 from time import time 5 6 class MyThread(threading.Thread): 7 def __init__(self, func, args): 8 self.func = func 9 self.args = args 10 self.res = None 11 self.ts = 0 12 threading.Thread.__init__(self) 13 14 def run(self): 15 t1 = time() 16 self.res = apply(self.func, self.args) 17 t2 = time() 18 self.ts = t2 - t1 19 20 def getResult(self): 21 return self.res 22 23 def getts(self): 24 return self.ts
myThread.py
threading 模块中的其它函数
生产者-消费者问题和Queue 模块
Queue 模块可以用来进行线程间通讯,让各个线程之间共享数据。
1 #!/usr/bin/env python 2 3 from random import randint 4 from time import sleep 5 from Queue import Queue 6 from myThread import MyThread 7 8 def writeQ(queue): 9 print ‘producting object for Q...‘ 10 queue.put(‘product‘, 1) 11 print "size now %d" % queue.qsize() 12 13 def readQ(queue): 14 val = queue.get(1) 15 print ‘consumed object from Q... size now, %d‘ %16 queue.qsize() 17 18 def writer(queue, loops): 19 for i in range(loops): 20 writeQ(queue) 21 sleep(randint(1, 3)) 22 23 def reader(queue, loops): 24 for i in range(loops): 25 readQ(queue) 26 sleep(randint(2, 5)) 27 28 funcs = [writer, reader] 29 nfuncs = range(len(funcs)) 30 31 def main(): 32 nloops = randint(2, 5) 33 q = Queue(32) 34 35 threads = [] 36 for i in nfuncs: 37 t = MyThread(funcs[i], (q, nloops)) 38 threads.append(t) 39 40 for i in nfuncs: 41 threads[i].start() 42 43 for i in nfuncs: 44 threads[i].join() 45 46 print ‘all DONE‘ 47 48 if __name__ == ‘__main__‘: 49 main()
prodcons.py
18.6 相关模块
? concurrency with python threading, for parallel programming, please refer to mutliprocesses module