paramiko模块
基于SSH用于远程连接服务器并执行相关操作,paramiko.SSHClient()
通过scp可以传输文件,python基于scp来发送命令,paramiko.Transport(('hostname', port))
1 import paramiko 2 3 ssh = paramiko.SSHClient() # 实例化ssh对象 4 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 允许连接不在know_hosts文件中的主机 5 ssh.connect(hostname='bejsrtl200.bj.intel.com', username='jiawenyx', password='intel@7128') # 连接服务器 6 stdin, stdout, stderr = ssh.exec_command('ifconfig') # 执行命令 7 8 print(stdout.read().decode()) # 打印执行结果 9 10 ssh.close()
1 import paramiko 2 3 transport = paramiko.Transport(('bejsrtl184.bj.intel.com', 22)) 4 transport.connect(username='jiawenyx', password='intel@7128') 5 6 ssh = paramiko.SSHClient() 7 ssh._transport = transport 8 9 stdin, stdout, stderr = ssh.exec_command('ifconfig') 10 print(stdout.read().decode()) # 打印执行结果 11 12 transport.close()
1 import paramiko 2 transport = paramiko.Transport(('host',22)) 3 transport.connect(username='root',password='123') 4 sftp = paramiko.SFTPClient.from_transport(transport) 5 sftp.put('D:\Pycharm\hadoop_spark\ssh_files\id_rsa','/usr/local/id_rsa') # 文件名必须写上 6 transport.close()
进程
进程和程序:
程序:指令的集合。
进程:是一个执行中的程序,即将程序装载到内存中,系统为它分配资源的这一过程。进程是操作系统资源分配的基本单位。
区别:程序是进程运行的静态描述文本,进程是程序的一次执行活动,属于动态概念。程序和进程无一一对应关系,一个程序可由多个进程共用,一个进程在活动中又可顺序地执行若干个程序。进程是一个能独立运行的单位,能与其他进程并发执行,进程是作为资源申请和调度单位存在的;而通常的程序段不能作为一个独立运行的单位。
进程
每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。
进程状态
线程
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
python不管PC有几核,在同一核同一时刻执行的线程只有一个,python是调用系统的原生线程。
线程和进程的区别
进程要操作CPU,必须要先创建一个线程,所有在同一个进程里的线程是共享同一块内存空间的;
线程共享内存空间,进程的内存是独立的;
同一个进程的线程之间可以直接交流,两个进程想通信,必须通过一个中间代理来实现;
创建新线程很简单, 创建新进程需要对其父进程进行一次克隆;
一个线程可以控制和操作同一进程里的其他线程,但是进程只能操作子进程;
threading模块
线程两种调用方式:
直接调用
1 import threading 2 import time 3 4 5 def run(*args): # 线程要运行的函数 6 7 print('test',args) 8 time.sleep(3) 9 10 11 t1 = threading.Thread(target=run, args=('t1',)) 12 t2 = threading.Thread(target=run, args=('t2',)) 13 t1.start() 14 t2.start() 15 16 print(t1.getName()) 17 print(t2.getName()) 18 # Thread-1 19 # Thread-2
继承式调用
1 class MyThread(threading.Thread): 2 3 def __init__(self, n ): 4 super(MyThread, self).__init__() 5 self.n = n 6 7 def run(self): # 必须写run函数 8 print(self.n) 9 time.sleep(2) 10 11 t1 = MyThread('t1') 12 t2 = MyThread('t2') 13 t1.start() 14 t2.start() 15 16 print(t1.getName()) 17 print(t2.getName())
线程的一些方法
t.join(n) 表示主线程等待子线程多少时间,n表示主线程等待子线程的超时时间,如果在n时间内子线程未完成,主线程不在等待,执行后面的代码 t.run() 线程被cpu调度后自动执行线程对象的run方法(一般我们无需设置,除非自己定义类调用) t.start() 线程准备就绪,等待CPU调度 t.getName() 获取线程的名称 t.setName() 设置线程的名称 t.name 获取或设置线程的名称 t.is_alive() 判断线程是否为激活状态 t.isAlive() 判断线程是否为激活状态 t.isDaemon() 判断是否为守护线程 t.setDaemon() 是否设置守护线程,True表示主线程不等待子线程全部完成就执行后面的代码,False默认值,标识主线程等待子线程全部执行完后继续执行后面的代码
threading.current_thread() 当前线程详细信息
threading.active_count() 当前活跃线程数
threading.get_ident 获得线程号
线程运行顺序
主线程启动子线程后,两者之间运行是并行的,默认主线程不会等待子线程运行结束,创建完成后继续往下执行,执行完后等待子线程全部执行完后退出程序。
1 import threading 2 import time 3 4 5 def run(*args): # 线程要运行的函数 6 7 print('test',args) 8 time.sleep(3) 9 print(args, 'is done') 10 11 start_time = time.time() 12 for i in range(10): 13 t = threading.Thread(target=run, args=('t-%s' %i ,)) 14 t.start() 15 16 print("-------------------------------------------") 17 print('run_time = ', time.time()- start_time) 18 19 # test ('t-0',) 20 # test ('t-1',) 21 # test ('t-2',) 22 # test ('t-3',) 23 # test ('t-4',) 24 # test ('t-5',) 25 # test ('t-6',) 26 # test ('t-7',) 27 # test ('t-8',) 28 # test ('t-9',) 29 # ------------------------------------------- 30 # run_time = 0.0 31 # ('t-3',) is done 32 # ('t-2',) is done 33 # ('t-1',) is done 34 # ('t-0',) is done 35 # ('t-6',) is done 36 # ('t-5',) is done 37 # ('t-4',) is done 38 # ('t-7',) is done 39 # ('t-8',) is done 40 # ('t-9',) is done
加入join,主线程会等待子线程执行完毕后继续往下执行,如果主线程需要子线程的返回结果,可以使用join。
1 import threading 2 import time 3 4 5 def run(*args): # 线程要运行的函数 6 print('running',args) 7 time.sleep(3) 8 print(args, 'is done') 9 10 11 start_time = time.time() 12 thread_pool = [] 13 for i in range(10): 14 t = threading.Thread(target=run, args=('t-%s' %i ,)) 15 t.start() 16 thread_pool.append(t) 17 18 for i in thread_pool: 19 i.join() 20 21 print('------------------------------------------') 22 23 print('run_time = ', time.time()- start_time) 24 # 25 # running ('t-0',) 26 # running ('t-1',) 27 # running ('t-2',) 28 # running ('t-3',) 29 # running ('t-4',) 30 # running ('t-5',) 31 # running ('t-6',) 32 # running ('t-7',) 33 # running ('t-8',) 34 # running ('t-9',) 35 # ('t-1',) is done 36 # ('t-0',) is done 37 # ('t-9',) is done 38 # ('t-8',) is done 39 # ('t-7',) is done 40 # ('t-6',) is done 41 # ('t-5',) is done 42 # ('t-4',) is done 43 # ('t-3',) is done 44 # ('t-2',) is done 45 # ------------------------------------------ 46 # run_time = 3.0156748294830322
join 参数:timeout # 有n个设置join的子线程,就等待n倍timeout, 默认一直等待执行完毕。
当没有设置守护线程时,主线程等待N 倍timeout后继续往下执行, 主线程执行完毕,子线程依然可以继续执行,执行完毕后退出程序;对于守护线程则是到时间就kill子线程。
1 import threading 2 import time 3 def run(*args): # 线程要运行的函数 4 print('running',args) 5 time.sleep(3) 6 print(args, 'is done') 7 8 start_time = time.time() 9 thread_pool = [] 10 for i in range(5): 11 t = threading.Thread(target=run, args=('t-%s' %i ,)) 12 t.setDaemon(True) 13 t.start() 14 thread_pool.append(t) 15 16 for i in thread_pool: 17 i.join(0.5) # 有n个线程就等待n 倍的timeout 18 19 print('------------------------------------------') 20 21 print('run_time = ', time.time()- start_time) 22 23 # 24 # running ('t-0',) 25 # running ('t-1',) 26 # running ('t-2',) 27 # running ('t-3',) 28 # running ('t-4',) 29 # ------------------------------------------ 30 # run_time = 2.531254768371582 31 # 32 # Process finished with exit code 0
1 import threading 2 import time 3 def run(*args): # 线程要运行的函数 4 print('running',args) 5 time.sleep(3) 6 print(args, 'is done') 7 8 start_time = time.time() 9 thread_pool = [] 10 for i in range(5): 11 t = threading.Thread(target=run, args=('t-%s' %i ,)) 12 t.setDaemon(True) 13 t.start() 14 thread_pool.append(t) 15 16 for i in thread_pool: 17 i.join(0.6) # 有n个线程就等待n 倍的timeout 18 19 print('------------------------------------------') 20 21 print('run_time = ', time.time()- start_time) 22 23 # #running ('t-0',) 24 # running ('t-1',) 25 # running ('t-2',) 26 # running ('t-3',) 27 # running ('t-4',) 28 # ('t-2',) is done 29 # ('t-1',) is done 30 # ('t-0',) is done 31 # ('t-4',) is done 32 # ('t-3',) is done 33 # ------------------------------------------ 34 # run_time = 3.021475076675415 35 # 36 # Process finished with exit code 0
守护线程
为主线程服务,主线程退出,不必等待守护线程的结束。
t.setDaemon(True) 把当前线程设置成守护线程 ,在t.start()之前设置
由守护线程创建的子线程为守护线程,可以通过t.isDaemon来判断,详见代码 主线程_守护线程_子线程为什么会由守护线程创建的线程会直接退出。
1 import threading 2 import time 3 4 5 def run(*args): # 线程要运行的函数 6 7 print('test',args) 8 time.sleep(3) 9 print(args, 'is done') 10 11 start_time = time.time() 12 for i in range(10): 13 t = threading.Thread(target=run, args=('t-%s' %i ,)) 14 t.setDaemon(True) 15 t.start() 16 17 print("-------------------------------------------") 18 print('run_time = ', time.time()- start_time) 19 # 20 # test ('t-0',) 21 # test ('t-1',) 22 # test ('t-2',) 23 # test ('t-3',) 24 # test ('t-4',) 25 # test ('t-5',) 26 # test ('t-6',) 27 # test ('t-7',) 28 # test ('t-8',) 29 # test ('t-9',) 30 # ------------------------------------------- 31 # run_time = 0.0 32 # 33 # Process finished with exit code 0
1 import time 2 import threading 3 4 5 def run(n): 6 print('[%s]------running----\n' % n) 7 time.sleep(1) 8 print('[%s]------done----\n' % n) 9 10 11 def main(): 12 for i in range(3): 13 t = threading.Thread(target=run, args=[i, ]) 14 t.start() 15 t.join(0.5) 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=3) 23 print("---main thread done----") 24 25 26 # Running result 27 # [0]------running---- 28 # 29 # starting thread Thread-2 30 # [1]------running---- 31 # 32 # starting thread Thread-3 33 # [0]------done---- 34 # 35 # [2]------running---- 36 # 37 # starting thread Thread-4 38 # [1]------done---- 39 # 40 # ---main thread done---- 41 # 42 # Process finished with exit code 0
GIL: Global Interpreter Lock
无论你启多少个线程,你有多少个cpu, Python在执行的时候会淡定的在同一时刻只允许一个线程运行。
GIL
并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念,同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行。像其中的JPython就没有GIL。然而因为CPython是大部分环境下默认的Python执行环境。GIL并不是Python的特性,Python完全可以不依赖于GIL。
GIL对python多线程的影响: http://www.dabeaz.com/python/UnderstandingGIL.pdf
线程锁(互斥锁Mutex)
线程锁保证同一时刻只有一个线程修改内存空间的同一数据,GIL保证同一时刻只有一个线程在运行。
多线程同时修改同一数据,可能会导致数据最终结果不准确。
1 import time 2 import threading 3 4 5 def addNum(): 6 global num # 在每个线程中都获取这个全局变量 7 time.sleep(1) 8 num -= 1 # 对此公共变量进行-1操作 9 print('%s--get num:%s:'%(threading.current_thread().name,num )) 10 11 num = 5 # 设定一个共享变量 12 thread_list = [] 13 for i in range(5): 14 t = threading.Thread(target=addNum) 15 t.start() 16 thread_list.append(t) 17 18 for t in thread_list: # 等待所有线程执行完毕 19 t.join() 20 21 print('final num:', num)
如果需要多个线程去修改同一数据,则需要给数据加一个线程锁。python3.x不加锁也不会出问题,但是建议加。如果修改数据量比较大的话,容易产生串行。
1 import time 2 import threading 3 4 5 lock = threading.Lock() 6 def addNum(): 7 8 lock.acquire() 9 global num # 在每个线程中都获取这个全局变量 10 time.sleep(1) 11 num -= 1 # 对此公共变量进行-1操作 12 print('%s--get num:%s'%(threading.current_thread().name,num )) 13 lock.release() 14 15 num = 5 # 设定一个共享变量 16 thread_list = [] 17 for i in range(5): 18 t = threading.Thread(target=addNum) 19 t.start() 20 thread_list.append(t) 21 22 for t in thread_list: # 等待所有线程执行完毕 23 t.join() 24 25 print('final num:', num)
递归锁
一个锁里面包含了子锁。
1 import time 2 import threading 3 4 5 lock = threading.RLock() 6 def run2(): 7 global num2 8 lock.acquire() 9 num2 -= 1 10 lock.release() 11 12 13 def addNum(): 14 global num1 # 在每个线程中都获取这个全局变量 15 lock.acquire() 16 run2() 17 time.sleep(1) 18 num1 -= 1 # 对此公共变量进行-1操作 19 print('%s--get num1:%s,num2:%s'%(threading.current_thread().name,num1,num2)) 20 lock.release() 21 22 num1, num2 = 5, 9 # 设定一个共享变量 23 thread_list = [] 24 for i in range(5): 25 t = threading.Thread(target=addNum) 26 t.start() 27 thread_list.append(t) 28 29 30 while threading.active_count() != 1: 31 print(threading.active_count()) 32 time.sleep(1) 33 else: 34 print('----all threads done---') 35 print('final num:', num1, num2)
信号量:Semaphore
互斥锁允许同一时刻只有一个线程修改数据,Semaphore 允许同一时刻运行一定数量的线程。
1 import time 2 import threading 3 4 5 semaphore = threading.BoundedSemaphore(3) 6 7 def addNum(): 8 semaphore.acquire() 9 time.sleep(1) 10 print('%s---running'%threading.current_thread().name) 11 semaphore.release() 12 13 for i in range(10): 14 t = threading.Thread(target=addNum) 15 t.start() 16 17 18 while threading.active_count() != 1: 19 pass 20 else: 21 print('----all threads done---')
Events
红绿灯,一个线程充当交通指挥灯,多个线程充当车辆,按照红灯停绿灯行的规则。
注意event.set, event.clear放在打印灯和车的状态位置,否则容易出现红灯车也在跑的情况。
1 import threading, time, random 2 3 4 events = threading.Event() 5 6 def lighter(): 7 if not events.isSet(): 8 events.set() # 初始化绿灯Event set 9 counter = 0 10 while True: 11 if counter < 5: 12 print('\033[42;0mGreen is lighten...\033[0m') 13 elif counter < 10: 14 if events.isSet(): 15 events.clear() 16 print('\033[41;0mRed is lighten...\033[0m') 17 18 else: 19 counter = 0 20 21 print('\033[42;1m--green light on---\033[0m') 22 events.set() 23 time.sleep(1) 24 counter += 1 25 26 def car(i): 27 while True: 28 if events.isSet(): 29 print("car[%s] is running..."%i) 30 time.sleep(random.randrange(10)) 31 else: 32 print('car is waiting green lighten...') 33 events.wait() 34 if __name__ == '__main__': 35 lighter1 = threading.Thread(target=lighter) 36 lighter1.start() 37 for i in range(3): 38 t = threading.Thread(target=car, args=(i,)) 39 t.start()
Event对象通过设置/清除标志位来实现和其他线程的同步,例如交通灯来修改Event的标志位来控制车辆线程的状态。
Event的几大方法:
event.set: 设置标志位为True
event.clear: 清除标志位,标志位为False
event.wait: 如果标志位为True,则不做操作;否则一直阻塞至标志位被设置为True
event.isSet: 相当于event.is_set,如果标志位被设置,则返回True;否则返回False
queue 队列
class queue.Queue(maxsize=0) # 先入先出
class queue.LifoQueue(maxsize=0) # 后入先出
class queue.PriorityQueue(maxsize=0) # 按设置优先级获取数据
maxsize代表队列最多能存放的条目数,一旦达到maxsize队列将会阻塞,直到队列被被使用;0或者负数表示队列无穷大。
优先级原则:最低值的条目是先检索的,最低值的条目是排序后返回的条目(列表(条目))[0]。条目的典型模式是表单中的元组:(priority_number, data)。
Queue.qsize() # 返回队列大小
Queue.empty() # 如果队列为empty,则返回True
Queue.full() # 如果队列为full,则返回True
Queue.put(item, block=True, timeout=None)
将item插入队列,默认block为True, timeout为None,如果队列为full,则会阻塞知道队列条目被get。
如果timeout是一个正数,将会等待timeout 秒,队列仍然为full,则抛出Full exception;
如果block为false, 队列为full, 此时向队列插入数据时,直接抛出Full exception, 即使timeout设置为正数也将会被忽略。
Queue.put_nowait(item) 等价于 put(item, False)
Queue.get(block=True, timeout=None)
从队列中获取数据或者删除队列书中的数据,默认block为True,timeout为None,如果队列为空,则会阻塞直到队列为非空。
如果timeout是一个正数,将会等待timeout 秒,队列仍然为空,则抛出Empty exception;
如果block为false,队列为空,此时从队列获取数据时,直接抛出Empty exception,即使timeout设置为正数也将会被忽略。
Queue.get_nowait() 等价于 get(False)
exception queue.Empty
exception queue.Full
Queue.task_done()
Queue.join() block直到queue被消费完毕
https://docs.python.org/3.5/library/queue.html#queue.Queue.task_done
多线程的应用
python多线程不适合cpu密集操作型任务,适合io操作密集型的任务。
io操作不占用cpu,计算占用cpu。
多进程
多进程的定义和多线程类似。每个进程都拥有一个独立的内存空间,所以多进程需要较大的开销。
1 from multiprocessing import Process 2 import os 3 4 def run(i): 5 6 print('number: %s, process id: %s, parent id: %s, moudle name: %s'% 7 (i, os.getpid(),os.getppid(),__name__)) 8 9 p_num = [] 10 if __name__ == '__main__': # 多进程win 系统需要加这句,否则报错 11 for i in range(10): 12 p = Process(target=run, args=(i,)) 13 p.start() 14 p_num.append(p) 15 for p in p_num: 16 p.join() # 和多线程功能一样 17 18 print('main is done, moudle name: %s'%__name__)
number: 0, process id: 988, parent id: 25368, moudle name: __mp_main__ number: 1, process id: 5240, parent id: 25368, moudle name: __mp_main__ number: 3, process id: 21328, parent id: 25368, moudle name: __mp_main__ number: 2, process id: 24892, parent id: 25368, moudle name: __mp_main__ number: 4, process id: 25276, parent id: 25368, moudle name: __mp_main__ number: 5, process id: 24612, parent id: 25368, moudle name: __mp_main__ number: 7, process id: 12340, parent id: 25368, moudle name: __mp_main__ number: 6, process id: 24464, parent id: 25368, moudle name: __mp_main__ number: 8, process id: 20040, parent id: 25368, moudle name: __mp_main__ number: 9, process id: 20796, parent id: 25368, moudle name: __mp_main__ main is done, moudle name: __main__ Process finished with exit code 0
进程间通信
同一进程内的线程共享进程内存空间,可以直接访问同一数据;不同进程都拥有一个独立内存空间,要想实现两个进程间的数据交换,可通过以下方法:
Queue
使用方法和线程queue一样,线程中的queue不能实现进程中的数据通信。
1 from multiprocessing import Process, Queue 2 import os 3 4 def run(q): 5 6 print('process id: %s'% os.getpid()) 7 q.put(os.getpid()) 8 9 p_num = [] 10 if __name__ == '__main__': # 多进程win 系统需要加这句,否则报错 11 q = Queue() 12 for i in range(10): 13 p = Process(target=run, args=(q,)) 14 p.start() 15 p_num.append(p) 16 for p in p_num: 17 p.join() # 和多线程功能一样 18 19 for i in range(10): 20 print('main is done, q = %s'% q.get())
process id: 12976 process id: 27556 process id: 11424 process id: 28284 process id: 29056 process id: 14728 process id: 22856 process id: 26928 process id: 28496 process id: 6612 main is done, q = 27556 main is done, q = 12976 main is done, q = 11424 main is done, q = 28284 main is done, q = 29056 main is done, q = 14728 main is done, q = 22856 main is done, q = 26928 main is done, q = 28496 main is done, q = 6612 Process finished with exit code 0
Pipe
由Pipe()返回的两个连接对象表示管道的两端,每个连接对象都有send()和recv()方法。
注意,如果两个进程(或线程)试图同时读取或写入管道的同一端口,那么管道中的数据可能会被损坏;在同时使用不同端口的过程中也不会有风险。
1 from multiprocessing import Process, Pipe 2 import os 3 4 5 6 def run(conn): 7 conn.send('process id: %s'%os.getpid()) 8 conn.send('process done') 9 conn.close() 10 print('child process id: %s'% os.getpid()) 11 12 13 if __name__ == '__main__': 14 parent_conn, child_conn = Pipe() 15 p = Process(target=run, args=(child_conn,)) 16 p.start() 17 print(parent_conn.recv()) 18 print(parent_conn.recv()) 19 # child process id: 10816 20 # process id: 10816 21 # process done
Manager
Manager 支持list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array等数据类型。
1 from multiprocessing import Process, Manager 2 import os 3 4 def f(d, l): 5 d['%s parent is %s'%(os.getpid(), os.getppid())] = os.getppid() 6 l.append(os.getpid()) 7 8 9 10 if __name__ == '__main__': 11 with Manager() as manager: 12 d = manager.dict() 13 l = manager.list(range(3)) 14 p_list = [] 15 for i in range(5): 16 p = Process(target=f, args=(d, l)) 17 p.start() 18 p_list.append(p) 19 for res in p_list: 20 res.join() 21 22 print(list(d.keys())) # 以list类型返回字典的key 23 print(l) 24 25 # ['26500 parent is 29096', '23932 parent is 29096', '29540 parent is 29096', '27728 parent is 29096', '15348 parent is 29096'] 26 # [0, 1, 2, 26500, 27728, 15348, 23932, 29540]
进程同步:进程锁
进程锁用来锁定输出,否则容易混淆,比如一个进程输出没完,另一个进程继续输出。
1 from multiprocessing import Process, Lock 2 3 4 def f(l, i): 5 l.acquire() 6 print('hello world', i) 7 l.release() 8 9 if __name__ == '__main__': 10 lock = Lock() 11 12 for num in range(10): 13 Process(target=f, args=(lock, num)).start()
进程池
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进程,那么程序就会等待,直到进程池中有可用进程为止。
- apply 从进程池中获取的进程串行运行
- apply_async 从进程池中获取的进程并行运行
1 from multiprocessing import Pool 2 import time, os 3 4 5 def f1(arg): 6 time.sleep(1) 7 print('process id %s:'%os.getppid(),arg) 8 return arg # 传给Bar 9 10 def Bar(args): # 由父进程执行 11 print('%s execute %s'%(os.getppid(),args)) 12 13 14 15 if __name__ == '__main__': 16 pool = Pool(5) 17 for i in range(10): 18 # pool.apply(func=f1,args=(i,)) # 所有进程串行执行 19 pool.apply_async(func=f1, args=(i,), callback=Bar) # 异步并行执行 20 21 pool.close() # 等待所有的任务执行完毕 22 23 # pool.terminate() # 立即终止子进程的任务,主进程继续执行 24 pool.join() # 执行pool.join时必须先执行pool.close或者pool.terminate 25 # 进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭close,terminate也无效 26 print('end') 27 28 # process id 8360: 0 29 # 19884 execute 0 30 # process id 8360: 1 31 # 19884 execute 1 32 # process id 8360: 2 33 # 19884 execute 2 34 # process id 8360: 3 35 # 19884 execute 3 36 # process id 8360: 4 37 # 19884 execute 4 38 # process id 8360: 5 39 # 19884 execute 5 40 # process id 8360: 6 41 # 19884 execute 6 42 # process id 8360: 7 43 # 19884 execute 7 44 # process id 8360: 8 45 # 19884 execute 8 46 # process id 8360: 9 47 # 19884 execute 9 48 # end 49 # 50 # Process finished with exit code 0