python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。

Python提供了非常好用的多进程包multiprocessing,只需要定义一个函数,Python会完成其他所有事情。

1.Process

创建进程的类:Process([group [, target [, name [, args [, kwargs]]]]]),target表示调用对象,args表示调用对象的位置参数元组。kwargs表示调用对象的字典。name为别名。group实质上不使用。
方法:is_alive()、join([timeout])、run()、start()、terminate()。其中,Process以start()启动某个进程。

属性:authkey、daemon(要通过start()设置)、exitcode(进程在运行时为None、如果为–N,表示被信号N结束)、name、pid。其中daemon是父进程终止后自动终止,且自己不能产生新进程,必须在start()之前设置。

 

  (1)创建多进程



1 #!/usr/bin/env python
 2 # -*- coding: utf-8 -*-
 3 
 4 from multiprocessing import Process
 5 import time
 6 
 7 
 8 def f(name):
 9     time.sleep(1)
10     print("hello ", name)
11 
12 if __name__ == '__main__':
13     p1 = Process(target=f, args=['Bob1', ])     #target是创建的进程要执行的函数,args是传递给该函数的参数
14     p2 = Process(target=f, args=['Bob2', ])   #这里进程p*的父进程是当前脚本,脚本的父进程是执行环境(eg:pycharm)
15     p1.start()
16     p2.start()
17     p1.join()
18     p2.join()
19     time.sleep(1)
20     print("----main process over----")



  执行结果



hello  Bob2
hello  Bob1
----main process over----



 

2、LOCK

  当多个进程需要访问共享资源的时候,LOCK可以用来避免访问的冲突 



1 #!/usr/bin/env python
 2 # -*- coding: utf-8 -*-
 3 
 4 '''
 5 进程同步是说的进程一起执行
 6 '''
 7 from multiprocessing import Process,Lock
 8 
 9 def f(lock,i):
10     lock.acquire()
11     try:
12         print('hello world',i)
13     finally:
14         lock.release()
15 
16 if __name__ == '__main__':
17     lock = Lock()
18 
19     for num in range(10):
20         Process(target=f,args=(lock,num)).start()



  执行结果



hello world 0
hello world 3
hello world 2
hello world 6
hello world 5
hello world 1
hello world 4
hello world 7
hello world 9
hello world 8



 

3、Queue、Pipe、Manager

  分别利用Queue、Pipe、Manager实现进程间通信

 (1)Queue

  Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。



  get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常。



 



  (2) Pipe



  Pipe方法返回(conn1, conn2)代表一个管道的两个端。Pipe方法有duplex参数,如果duplex参数为True(默认值),那么这个管道是全双工模式,也就是说conn1和conn2均可收发。duplex为False,conn1只负责接受消息,conn2只负责发送消息。



   send和recv方法分别是发送和接受消息的方法。例如,在全双工模式下,可以调用conn1.send发送消息,conn1.recv接收消息。如果没有消息可接收,recv方法会一直阻塞。如果管道已经被关闭,那么recv方法会抛出EOFError。 



 



  (3) Manager



  Manager()支持:list、dict、Namespace、Lock、Rlock、Semaphore、 BoundedSemaphore、Condition、Event、Barrier、Queue、Value、Array 其中常用的就list、dict。 注意:其中的list、dict都要通过Manager() 实现。



 



#!/usr/bin/env python
# -*- coding: utf-8 -*-

'''
进程之间是不能直接通信的,需要第三方作为通信媒介
'''

'''以下是Queue实现数据传递代码'''
from multiprocessing import  Process,Queue

def f1(q):
    q.put([22,'root','hello'])

def f2(q):
    q.put([23,'admin','hello'])


'''以下是Pipe实现数据传递代码'''
from multiprocessing import  Process,Pipe

def f3(conn):
    conn.send([23,'admin1','hello'])

def f4(conn):
    conn.send([24, 'root', 'hello'])


'''以下是Manager实现数据共享代码'''
from multiprocessing import Process,Manager

def f(d,l,n):
    d[n] = n
    d['name'] = 'root'
    l.append(n)
    print(l)

#以下是主程序
if __name__ == '__main__':
    print(
        '''\033[42;1m 利用Queue实现进程间的通信 \033[0m '''
    )
    '''
    这里的进程间通信是在同一父进程下不同子进程间的通信,因为队列q是在父进程实例化并
    传递到每个子进程中。(当然可以在一个第三方文件里面创建一个队列,其余文件来调用是可以的)
    '''
    q = Queue()    #生成一个第三方队列,用来存放子进程put的值
                # 这里的Queue是在queue.Queue的基础上,封装了能多进程访问
    pq1 = Process(target=f1,args=(q,))
    pq2 = Process(target=f2,args=(q,))  #要把队列对象q当参数传递给各个子进程
    pq1.start()
    pq2.start()
    print('from parent 1:',q.get())
    print('from parent 2:',q.get())

    print(
        '''\n\033[43;1m 利用Pipe实现进程间的通信 \033[0m '''
    )
    parent_conn,child_conn = Pipe()
    pp1 = Process(target=f3,args=(child_conn,))
    pp2 = Process(target=f4,args=(child_conn,))
    pp1.start()
    pp2.start()
    print('parent data from pp1:', parent_conn.recv())
    print('parent data from pp2:', parent_conn.recv())
    parent_conn.close() #这个在子进程或父进程关闭都可以

    print(
        '''\n\033[43;1m 利用Manager实现进程间的数据共享 \033[0m '''
    )
    '''
    这里Manager()支持:list、dict、Namespace、Lock、Rlock、Semaphore、
     BoundedSemaphore、Condition、Event、Barrier、Queue、Value、Array
     其中常用的就list、dict。
     注意:其中的list、dict都要通过Manager() 实现
    '''
    #manager = Manager()  #注意这样后面需要关闭,用with...as...语句更方便
    with Manager() as manager:
        d = manager.dict()
        # l = manager.list()  #这样生成空列表
        l = manager.list(range(5))

        process_list = []
        for i in range(10):
            p = Process(target=f,args=(d,l,i))
            p.start()
            process_list.append(p)

        for res in process_list:
            res.join()

        print("执行结束l:",l)
        print("执行结束d:",d)



 

4、POOL

  在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间。当被操作对象数目不大时,可以直接利用multiprocessing中的Process动态成生多个进程,十几个还好,但如果是上百个,上千个目标,手动的去限制进程数量却又太过繁琐,此时可以发挥进程池的功效。
Pool可以提供指定数量的进程,供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来它。

函数解释:

  • apply_async(func[, args[, kwds[, callback]]]) 它是非阻塞,apply(func[, args[, kwds]])是阻塞的(理解区别,看例1例2结果区别)
  • close()    关闭pool,使其不在接受新的任务。
  • terminate()    结束工作进程,不在处理未完成的任务。
  • join()    主进程阻塞,等待子进程的退出, join方法要在close或terminate之后使用。
1 #!/usr/bin/env python
 2 # -*- coding: utf-8 -*-
 3 
 4 '''
 5 进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有
 6 可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。
 7 
 8 进程池方法:
 9     apply: 同步
10     apply_async: 异步
11 '''
12 from multiprocessing import Process,Pool,freeze_support
13 import time
14 
15 def Foo(i):
16     time.sleep(1)
17     return i,i+100
18 
19 def Bar(arg):
20     # print('arg:',arg)
21     data.append(arg)
22     print('--> exec done [%s]: %s' % (arg[0],arg[1]))
23 
24 if __name__ == '__main__':
25     freeze_support()   #要导入,在win下必须有它,不然会报错。linux下不用
26 
27     pool = Pool(3)  #生成一个有5个进程序列的进程池实例
28     data = []
29     for i in range(10):
30         # func为进程每次执行的函数,args是参数,callback是执行完func后执行的函数,
31         # 并且callback会自动接收func的返回值作为函数参数。Foo是子进程执行的函数,callback是在
32         # 父进程执行的函数,所以这里说明是子进程把执行结果返回给父进程里面的函数
33         pool.apply_async(func=Foo,args=(i,),callback=Bar)
34         #pool.apply(func=Foo,args=(i,))   #进程同步,没啥用
35 
36     print("end")
37     pool.close()
38     pool.join()  #这里表示进程池中进程执行完毕后再关闭,若注释,那么程序直接关闭
39         #注意:这里是先close,再join
40     print('全局变量data:',data)



  执行结果



end
--> exec done [0]: 100
--> exec done [1]: 101
--> exec done [2]: 102
--> exec done [3]: 103
--> exec done [4]: 104
--> exec done [5]: 105
--> exec done [7]: 107
--> exec done [6]: 106
--> exec done [8]: 108
--> exec done [9]: 109
全局变量data: [(0, 100), (1, 101), (2, 102), (3, 103), (4, 104), (5, 105), (7, 107), (6, 106), (8, 108), (9, 109)]



 

5、Semaphore

  Semaphore用来控制对共享资源的访问数量,例如池的最大连接数。



1 #!/usr/bin/env python
 2 # -*- coding:utf8 -*-
 3 
 4 import multiprocessing
 5 import time
 6 
 7 
 8 def run(s, i):
 9     s.acquire()
10     print(multiprocessing.current_process().name + "acquire");
11     time.sleep(i)
12     print(multiprocessing.current_process().name + "release\n");
13     s.release()
14 
15 
16 if __name__ == "__main__":
17     s = multiprocessing.Semaphore(2)  #最多允许3个进程同时运行
18 
19     process_list = []
20     for i in range(3):
21         p = multiprocessing.Process(target=run, args=(s, i * 2))
22         p.start()
23         process_list.append(p)
24 
25     for process in process_list:
26         process.join()
27 
28     print('\033[31;1m 全部结束 \033[0m')



  执行结果



Process-1acquire
Process-1release

Process-3acquire
Process-2acquire
Process-2release

Process-3release

 全部结束



 

6、Event

  用Event实现进程同步



1 #!/usr/bin/env python
 2 # -*- coding:utf8 -*-
 3 
 4 import multiprocessing
 5 import time
 6 
 7 
 8 def wait_for_event(e):
 9     print("wait_for_event: starting")
10     e.wait()
11     print("wairt_for_event: e.is_set()->" + str(e.is_set()))
12 
13 
14 def wait_for_event_timeout(e, t):
15     print("wait_for_event_timeout:starting")
16     e.wait(t)
17     print("wait_for_event_timeout:e.is_set->" + str(e.is_set()))
18 
19 
20 if __name__ == "__main__":
21     e = multiprocessing.Event()
22     w1 = multiprocessing.Process(name="block",
23                                  target=wait_for_event,
24                                  args=(e,))
25 
26     w2 = multiprocessing.Process(name="non-block",
27                                  target=wait_for_event_timeout,
28                                  args=(e, 2))
29     w1.start()
30     w2.start()
31 
32     time.sleep(3)
33 
34     e.set()
35     print("main: event is set")



  执行结果



wait_for_event: starting
wait_for_event_timeout:starting
wait_for_event_timeout:e.is_set->False
wairt_for_event: e.is_set()->True
main: event is set