程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。
在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了 CPU 的利用率。进程的出现让每个用户感觉到自己独享 CPU,因此,进程就是为了在 CPU 上实现多道编程而提出的。
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务
进程有很多优点,它提供了多道编程,让我们感觉我们每个人都拥有自己的 CPU 和其他资源,可以提高计算机的利用率。很多人就不理解了,既然进程这么优秀,为什么还要线程呢?其实,仔细观察就会发现进程还是有很多缺陷的,主要体现在两点上:
例如,我们在使用 qq 聊天, qq 做为一个独立进程如果同一时间只能干一件事,那他如何实现在同一时刻 即能监听键盘输入、又能监听其它人给你发的消息、同时还能把别人发的消息显示在屏幕上呢?你会说,操作系统不是有分时么?但我的亲,分时是指在不同进程间的分时呀, 即操作系统处理一会你的 qq 任务,又切换到 word 文档任务上了,每个 cpu 时间片分给你的 qq 程序时,你的 qq 还是只能同时干一件事呀。
再直白一点, 一个操作系统就像是一个工厂,工厂里面有很多个生产车间,不同的车间生产不同的产品,每个车间就相当于一个进程,且你的工厂又穷,供电不足,同一时间只能给一个车间供电,为了能让所有车间都能同时生产,你的工厂的电工只能给不同的车间分时供电,但是轮到你的 qq 车间时,发现只有一个干活的工人,结果生产效率极低,为了解决这个问题,应该怎么办呢?。。。。没错,你肯定想到了,就是多加几个工人,让几个人工人并行工作,这每个工人,就是线程!
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython's memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)
上面的核心意思就是,无论你启多少个线程,你有多少个 cpu, Python 在执行的时候会淡定的在同一时刻只允许一个线程运行,擦。。。,那这还叫什么多线程呀?
由于所有线程在 cpu 上都是分时间片执行的. 举个例子来说
首先需要明确的一点是
并不是 Python 的特性,它是在实现 Python 解析器 (CPython) 时所引入的一个概念。就好比 C++ 是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码。有名的编译器例如 GCC,INTEL C++,Visual C++ 等。Python 也一样,同样一段代码可以通过 CPython,PyPy,Psyco 等不同的 Python 执行环境来执行。像其中的 JPython 就没有 GIL。然而因为 CPython 是大部分环境下默认的 Python 执行环境。所以在很多人的概念里 CPython 就是 Python,也就想当然的把
- GIL
归结为 Python 语言的缺陷。所以这里要先明确一点:GIL 并不是 Python 的特性,Python 完全可以不依赖于 GIL
- GIL
这篇文章透彻的剖析了 GIL 对 python 多线程的影响,强烈推荐看一下:http://www.dabeaz.com/python/UnderstandingGIL.pdf
线程有 2 种调用方式,如下:
直接调用:
- 1 import threading
- 2 import time
- 3
- 4 def sayhi(num): #定义每个线程要运行的函数
- 5
- 6 print("running on number:%s" %num)
- 7
- 8 time.sleep(3)
- 9
- 10 if __name__ == '__main__':
- 11
- 12 t1 = threading.Thread(target=sayhi,args=(1,)) #生成一个线程实例
- 13 t2 = threading.Thread(target=sayhi,args=(2,)) #生成另一个线程实例
- 14
- 15 t1.start() #启动线程
- 16 t2.start() #启动另一个线程
- 17
- 18 print(t1.getName()) #获取线程名
- 19 print(t2.getName())
继承式调用
- 1 import threading
- 2 import time
- 3
- 4
- 5 class MyThread(threading.Thread):
- 6 def __init__(self,num):
- 7 threading.Thread.__init__(self)
- 8 self.num = num
- 9
- 10 def run(self):#定义每个线程要运行的函数
- 11
- 12 print("running on number:%s" %self.num)
- 13
- 14 time.sleep(3)
- 15
- 16 if __name__ == '__main__':
- 17
- 18 t1 = MyThread(1)
- 19 t2 = MyThread(2)
- 20 t1.start()
- 21 t2.start()
6.Join & Daemon
一些线程执行后台任务,如发送 keepalive 数据包,或执行周期性垃圾收集,或其他。 这些仅在主程序运行时有用,一旦其他非守护进程线程退出,就可以关闭它们。
没有守护线程,你必须跟踪它们,并告诉他们退出,然后你的程序才能完全退出。 通过将它们设置为守护线程,您可以让它们运行并忘记它们,当您的程序退出时,任何守护线程都会自动终止。
示例:
- 1 import time
- 2 import threading
- 3
- 4
- 5 def run(n):
- 6
- 7 print('[%s]------running----\n' % n)
- 8 time.sleep(2)
- 9 print('--done--')
- 10
- 11 def main():
- 12 for i in range(5):
- 13 t = threading.Thread(target=run,args=[i,])
- 14 t.start()
- 15 t.join(1)
- 16 print('starting thread', t.getName())
- 17
- 18
- 19 m = threading.Thread(target=main,args=[])
- 20 m.setDaemon(True) #将main线程设置为Daemon线程,它做为程序主线程的守护线程,当主线程退出时,m线程也会退出,由m启动的其它子线程会同时退出,不管是否执行完任务
- 21 m.start()
- 22 m.join(timeout=2)
- 23 print("---main thread done----")
注意: 守护线程在关机时突然停止。 他们的资源(如打开的文件,数据库事务等)可能无法正确释放。 如果你想让你的线程正常停止,使他们非守护进程,并使用一个合适的信号机制,如一个事件。
7. 线程锁 (互斥锁 Mutex)
一个进程下可以启动多个线程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问同一份数据,此时,如果 2 个线程同时要修改同一份数据,会出现什么状况?
未加锁版本 in python2
- 1 import time
- 2 import threading
- 3
- 4 def addNum():
- 5 global num #在每个线程中都获取这个全局变量
- 6 print('--get num:',num )
- 7 time.sleep(1)
- 8 num -=1 #对此公共变量进行-1操作
- 9
- 10 num = 100 #设定一个共享变量
- 11 thread_list = []
- 12 for i in range(100):
- 13 t = threading.Thread(target=addNum)
- 14 t.start()
- 15 thread_list.append(t)
- 16
- 17 for t in thread_list: #等待所有线程执行完毕
- 18 t.join()
- 19
- 20 print('final num:', num )
正常来讲,这个 num 结果应该是 0, 但在 python 2.7 上多运行几次,会发现,最后打印出来的 num 结果不总是 0,为什么每次运行的结果不一样呢? 哈,很简单,假设你有 A,B 两个线程,此时都 要对 num 进行减 1 操作, 由于 2 个线程是并发同时运行的,所以 2 个线程很有可能同时拿走了 num=100 这个初始变量交给 cpu 去运算,当 A 线程去处完的结果是 99,但此时 B 线程运算完的结果也是 99,两个线程同时 CPU 运算的结果再赋值给 num 变量后,结果就都是 99。那怎么办呢? 很简单,每个线程在要修改公共数据时,为了避免自己在还没改完的时候别人也来修改此数据,可以给这个数据加一把锁, 这样其它线程想修改此数据时就必须等待你修改完毕并把锁释放掉后才能再访问此数据。
* 注:不要在 3.x 上运行,不知为什么,3.x 上的结果总是正确的,可能是自动加了锁
加锁版本
- 1 import time
- 2 import threading
- 3
- 4 def addNum():
- 5 global num #在每个线程中都获取这个全局变量
- 6 print('--get num:',num )
- 7 time.sleep(1)
- 8 lock.acquire() #修改数据前加锁
- 9 num -=1 #对此公共变量进行-1操作
- 10 lock.release() #修改后释放
- 11
- 12 num = 100 #设定一个共享变量
- 13 thread_list = []
- 14 lock = threading.Lock() #生成全局锁
- 15 for i in range(100):
- 16 t = threading.Thread(target=addNum)
- 17 t.start()
- 18 thread_list.append(t)
- 19
- 20 for t in thread_list: #等待所有线程执行完毕
- 21 t.join()
- 22
- 23 print('final num:', num )
GIL VS Lock
既然 Python 已经有一个 GIL 来保证同一时间只能有一个线程来执行了,为什么这里还需要 lock? 注意啦,这里的 lock 是用户级的 lock, 跟那个 GIL 没关系 ,具体我们通过下图来看一下
既然用户程序已经自己有锁了,那为什么 C python 还需要 GIL 呢?加入 GIL 主要的原因是为了降低程序的开发的复杂度,比如现在的你写 python 不需要关心内存回收的问题,因为 Python 解释器帮你自动定期进行内存回收,你可以理解为 python 解释器里有一个独立的线程,每过一段时间它起 wake up 做一次全局轮询看看哪些内存数据是可以被清空的,此时你自己的程序 里的线程和 py 解释器自己的线程是并发运行的,假设你的线程删除了一个变量,py 解释器的垃圾回收线程在清空这个变量的过程中的 clearing 时刻,可能一个其它线程正好又重新给这个还没来及得清空的内存空间赋值了,结果就有可能新赋值的数据被删除了,为了解决类似的问题,python 解释器简单粗暴的加了锁,即当一个线程运行时,其它人都不能动,这样就解决了上述的问题, 这可以说是 Python 早期版本的遗留问题。
8.RLock(递归锁)
说白了就是在一个大锁中还要再包含子锁
- 1 import threading,time
- 2
- 3 def run1():
- 4 print("grab the first part data")
- 5 lock.acquire()
- 6 global num
- 7 num +=1
- 8 lock.release()
- 9 return num
- 10 def run2():
- 11 print("grab the second part data")
- 12 lock.acquire()
- 13 global num2
- 14 num2+=1
- 15 lock.release()
- 16 return num2
- 17 def run3():
- 18 lock.acquire()
- 19 res = run1()
- 20 print('--------between run1 and run2-----')
- 21 res2 = run2()
- 22 lock.release()
- 23 print(res,res2)
- 24
- 25
- 26 if __name__ == '__main__':
- 27
- 28 num,num2 = 0,0
- 29 lock = threading.RLock()
- 30 for i in range(10):
- 31 t = threading.Thread(target=run3)
- 32 t.start()
- 33
- 34 while threading.active_count() != 1:
- 35 print(threading.active_count())
- 36 else:
- 37 print('----all threads done---')
- 38 print(num,num2)
9.Semaphore(信号量)
互斥锁 同时只允许一个线程更改数据,而 Semaphore 是同时允许一定数量的线程更改数据 ,比如厕所有 3 个坑,那最多只允许 3 个人上厕所,后面的人只能等里面有人出来了才能再进去。
- 1 import threading,time
- 2
- 3 def run(n):
- 4 semaphore.acquire()
- 5 time.sleep(1)
- 6 print("run the thread: %s\n" %n)
- 7 semaphore.release()
- 8
- 9 if __name__ == '__main__':
- 10
- 11 num= 0
- 12 semaphore = threading.BoundedSemaphore(5) #最多允许5个线程同时运行
- 13 for i in range(20):
- 14 t = threading.Thread(target=run,args=(i,))
- 15 t.start()
- 16
- 17 while threading.active_count() != 1:
- 18 pass #print threading.active_count()
- 19 else:
- 20 print('----all threads done---')
- 21 print(num)
此类表示应在一定时间过去后才应运行的操作
与线程一样,通过调用它们的 start()方法来启动计时器。 可以通过调用 thecancel()方法停止计时器(在动作开始之前)。 定时器在执行其操作之前将等待的间隔可能与用户指定的间隔不完全相同。
- 1 def hello():
- 2 print("hello, world")
- 3
- 4 t = Timer(30.0, hello)
- 5 t.start() # after 30 seconds, "hello, world" will be printed
11.Events
事件是一个简单的同步对象;
该事件表示内部标志和线程
可以等待标志被设置,或者设置或清除标志本身。
event = threading.Event()
#一个客户线程可以等待设置的标志
event.wait()
#一个服务器线程可以设置或重置它
event.set()
event.clear()
如果设置了标志,wait 方法不会做任何事情。
如果该标志被清除,等待将阻塞,直到它再次被设置。
任何数量的线程都可以等待相同的事件。
通过 Event 来实现两个或多个线程间的交互,下面是一个红绿灯的例子,即起动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红灯停,绿灯行的规则。
- 1 import threading,time
- 2 import random
- 3 def light():
- 4 if not event.isSet():
- 5 event.set() #wait就不阻塞 #绿灯状态
- 6 count = 0
- 7 while True:
- 8 if count < 10:
- 9 print('\033[42;1m--green light on---\033[0m')
- 10 elif count <13:
- 11 print('\033[43;1m--yellow light on---\033[0m')
- 12 elif count <20:
- 13 if event.isSet():
- 14 event.clear()
- 15 print('\033[41;1m--red light on---\033[0m')
- 16 else:
- 17 count = 0
- 18 event.set() #打开绿灯
- 19 time.sleep(1)
- 20 count +=1
- 21 def car(n):
- 22 while 1:
- 23 time.sleep(random.randrange(10))
- 24 if event.isSet(): #绿灯
- 25 print("car [%s] is running.." % n)
- 26 else:
- 27 print("car [%s] is waiting for the red light.." %n)
- 28 if __name__ == '__main__':
- 29 event = threading.Event()
- 30 Light = threading.Thread(target=light)
- 31 Light.start()
- 32 for i in range(3):
- 33 t = threading.Thread(target=car,args=(i,))
- 34 t.start()
这里还有一个 event 使用的例子,员工进公司门要刷卡, 我们这里设置一个线程是 "门", 再设置几个线程为 "员工",员工看到门没打开,就刷卡,刷完卡,门开了,员工就可以通过。
刷卡例子
- 1 import threading
- 2 import time
- 3 import random
- 4
- 5 def door():
- 6 door_open_time_counter = 0
- 7 while True:
- 8 if door_swiping_event.is_set():
- 9 print("\033[32;1mdoor opening....\033[0m")
- 10 door_open_time_counter +=1
- 11
- 12 else:
- 13 print("\033[31;1mdoor closed...., swipe to open.\033[0m")
- 14 door_open_time_counter = 0 #清空计时器
- 15 door_swiping_event.wait()
- 16
- 17
- 18 if door_open_time_counter > 3:#门开了已经3s了,该关了
- 19 door_swiping_event.clear()
- 20
- 21 time.sleep(0.5)
- 22
- 23
- 24 def staff(n):
- 25
- 26 print("staff [%s] is comming..." % n )
- 27 while True:
- 28 if door_swiping_event.is_set():
- 29 print("\033[34;1mdoor is opened, passing.....\033[0m")
- 30 break
- 31 else:
- 32 print("staff [%s] sees door got closed, swipping the card....." % n)
- 33 print(door_swiping_event.set())
- 34 door_swiping_event.set()
- 35 print("after set ",door_swiping_event.set())
- 36 time.sleep(0.5)
- 37 door_swiping_event = threading.Event() #设置事件
- 38
- 39
- 40 door_thread = threading.Thread(target=door)
- 41 door_thread.start()
- 42
- 43
- 44
- 45 for i in range(5):
- 46 p = threading.Thread(target=staff,args=(i,))
- 47 time.sleep(random.randrange(3))
- 48 p.start()
队列在线程编程中尤其有用,因为信息必须在多个线程之间安全地交换。
class queue.Queue(maxsize = 0)#先入先出 (FIFO)
class queue.LifoQueue(maxsize = 0)#last in fisrt out
class queue.PriorityQueue(maxsize = 0)#存储数据时可设置优先级的队列
maxsize 是一个整数,它设置了可以放入队列中的项目数量的上限。一旦达到此大小,插入就会阻塞,直到队列项被消耗。如果 maxsize 小于或等于零,队列大小是无限的。
首先检索最低值的条目(最低值的条目是由 sorted(list(entries))[0] 返回的条目。条目的典型模式是形式为 (priority_number, data) 的元组。
异常 queue.Empty
在非空的队列对象上调用非阻塞 get() (or get_nowait()) 时引发的异常。
异常队列
当非阻塞 put() (or put_nowait()) 在已满的 Queue 对象上调用时引发异常。
Queue.qsize()
Queue.empty()#return 如果为空,则返回 True
Queue.full() #return 如果已满
Queue.put(item, block=True, timeout=None)
将项目放入队列。如果可选的 args 块为 true 并且超时为 None(默认值),则在必要时阻塞,直到空闲插槽可用。如果 timeout 是一个正数,它会阻塞最多超时秒数,并且如果在该时间内没有可用空闲时间,则会引发完全异常。否则(块为假),如果空闲时隙立即可用,则将一个项目放在队列上,否则抛出完全异常(在这种情况下忽略超时)。
Queue.put_nowait(item)
相当于 put(item,False)。
Queue.get(block=True, timeout=None)
从队列中删除并返回项目。如果可选的 args 块为 true 并且 timeout 为 None(默认值),则在必要时阻止,直到项目可用。如果 timeout 是一个正数,它会阻塞最多超时秒数,并且如果在该时间内没有可用的项目,则会引发 Empty 异常。否则(块为假),返回一个项,如果一个立即可用,否则提出空异常(在这种情况下超时被忽略)。
Queue.get_nowait()
相当于 get(False)。
提供了两种方法来支持跟踪入队任务是否已由守护程序消费者线程完全处理。
Queue.task_done()
指示以前入队的任务已完成。由队列消费者线程使用。对于用于获取任务的每个 get(),对 task_done()的后续调用将告诉队列任务上的处理已完成。
如果 join()当前正在阻塞,它将在所有项目都被处理时恢复(意味着对于已经被 put()到队列中的每个项目都接收到一个 task_done()调用)。
如果调用的次数比在队列中放置的项目多,则引发 ValueError。
Queue.join()block 直到 queue 被消费完毕
在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。
为什么要使用生产者和消费者模式
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。
什么是生产者消费者模式
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
下面来看一个最基本的生产者消费者模型的例子
- 1 import threading
- 2 import queue
- 3
- 4 def producer():
- 5 for i in range(10):
- 6 q.put("骨头 %s" % i )
- 7 print("开始等待所有的骨头被取走...")
- 8 q.join()
- 9 print("所有的骨头被取完了...")
- 10
- 11 def consumer(n):
- 12 while q.qsize() >0:
- 13 print("%s 取到" %n , q.get())
- 14 q.task_done() #告知这个任务执行完了
- 15
- 16 q = queue.Queue()
- 17
- 18 p = threading.Thread(target=producer,)
- 19 p.start()
- 20
- 21 c1 = consumer("李闯")
- 1 import time,random
- 2 import queue,threading
- 3 q = queue.Queue()
- 4 def Producer(name):
- 5 count = 0
- 6 while count <20:
- 7 time.sleep(random.randrange(3))
- 8 q.put(count)
- 9 print('Producer %s has produced %s baozi..' %(name, count))
- 10 count +=1
- 11 def Consumer(name):
- 12 count = 0
- 13 while count <20:
- 14 time.sleep(random.randrange(4))
- 15 if not q.empty():
- 16 data = q.get()
- 17 print(data)
- 18 print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data))
- 19 else:
- 20 print("-----no baozi anymore----")
- 21 count +=1
- 22 p1 = threading.Thread(target=Producer, args=('A',))
- 23 c1 = threading.Thread(target=Consumer, args=('B',))
- 24 p1.start()
- 25 c1.start()
multiprocessing 是一个包,它支持使用类似于线程模块的 API 来生成进程。 多进程包提供本地和远程并发,通过使用子进程而不是线程有效地避开全局解释器锁。 因此,多处理模块允许编程人员充分利用给定机器上的多个处理器。 它能在 Unix 和 Windows 上运行。
- 1 from multiprocessing import Process
- 2 import time
- 3 def f(name):
- 4 time.sleep(2)
- 5 print('hello', name)
- 6
- 7 if __name__ == '__main__':
- 8 p = Process(target=f, args=('bob',))
- 9 p.start()
- 10 p.join()
要显示涉及的单个进程 ID,下面是一个扩展示例:
- 1 from multiprocessing import Process
- 2 import os
- 3
- 4 def info(title):
- 5 print(title)
- 6 print('module name:', __name__)
- 7 print('parent process:', os.getppid())
- 8 print('process id:', os.getpid())
- 9 print("\n\n")
- 10
- 11 def f(name):
- 12 info('\033[31;1mfunction f\033[0m')
- 13 print('hello', name)
- 14
- 15 if __name__ == '__main__':
- 16 info('\033[32;1mmain process line\033[0m')
- 17 p = Process(target=f, args=('bob',))
- 18 p.start()
- 19 p.join()
不同进程间内存是不共享的,要想实现两个进程间的数据交换,可以用以下方法:
Queues
使用方法跟 threading 里的 queue 差不多
- 1 from multiprocessing import Process, Queue
- 2
- 3 def f(q):
- 4 q.put([42, None, 'hello'])
- 5
- 6 if __name__ == '__main__':
- 7 q = Queue()
- 8 p = Process(target=f, args=(q,))
- 9 p.start()
- 10 print(q.get()) # prints "[42, None, 'hello']"
- 11 p.join()
Pipes
Pipe()函数返回通过管道连接的一对连接对象,默认情况下是 duplex (two-way)。 例如:
- 1 from multiprocessing import Process, Pipe
- 2
- 3 def f(conn):
- 4 conn.send([42, None, 'hello'])
- 5 conn.close()
- 6
- 7 if __name__ == '__main__':
- 8 parent_conn, child_conn = Pipe()
- 9 p = Process(target=f, args=(child_conn,))
- 10 p.start()
- 11 print(parent_conn.recv()) # prints "[42, None, 'hello']"
- 12 p.join()
Pipe()返回的两个连接对象表示管道的两端。 每个连接对象都有 send()和 recv()方法(等等)。 请注意,如果两个进程(或线程)尝试同时读取或写入管道的同一端,则管道中的数据可能会损坏。 当然,同时使用管道的不同端的进程没有 corruption(损坏的意思?) 的风险。
16.Managers
Manager()返回的管理器对象控制一个保存 Python 对象的服务器进程,并允许其他进程使用代理来操作它们。
Manager()返回的管理器将支持类型列表,dict,Namespace,Lock,RLock,Semaphore,BoundedSemaphore,Condition,Event,Barrier,Queue,Value 和 Array。 例如,
- 1 from multiprocessing import Process, Manager
- 2
- 3 def f(d, l):
- 4 d[1] = '1'
- 5 d['2'] = 2
- 6 d[0.25] = None
- 7 l.append(1)
- 8 print(l)
- 9
- 10 if __name__ == '__main__':
- 11 with Manager() as manager:
- 12 d = manager.dict()
- 13
- 14 l = manager.list(range(5))
- 15 p_list = []
- 16 for i in range(10):
- 17 p = Process(target=f, args=(d, l))
- 18 p.start()
- 19 p_list.append(p)
- 20 for res in p_list:
- 21 res.join()
- 22
- 23 print(d)
- 24 print(l)
17. 进程同步
Without using the lock output from the different processes is liable to get all mixed up.
没有使用进程同步的时候, 输出的时候可能会导致输出混乱
- 1 from multiprocessing import Process, Lock
- 2
- 3 def f(l, i):
- 4 l.acquire()
- 5 try:
- 6 print('hello world', i)
- 7 finally:
- 8 l.release()
- 9
- 10 if __name__ == '__main__':
- 11 lock = Lock()
- 12
- 13 for num in range(10):
- 14 Process(target=f, args=(lock, num)).start()
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。
进程池中有两个方法:
- 1 from multiprocessing import Process,Pool
- 2 import time
- 3
- 4 def Foo(i):
- 5 time.sleep(2)
- 6 return i+100
- 7
- 8 def Bar(arg):
- 9 print('-->exec done:',arg)
- 10
- 11 pool = Pool(5)
- 12
- 13 for i in range(10):
- 14 pool.apply_async(func=Foo, args=(i,),callback=Bar)
- 15 #pool.apply(func=Foo, args=(i,))
- 16
- 17 print('end')
- 18 pool.close()
- 19 pool.join()#进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。
来源: