• python基础:multiprocessing的使用


    不同于C++或Java的多线程,python中是使用多进程来解决多项任务并发以提高效率的问题,依靠的是充分使用多核CPU的资源。这里是介绍mulitiprocessing的官方文档:https://docs.python.org/2/library/multiprocessing.html

    一、多进程并发效果演示

    [python] view plain copy
     
    1. <span style="font-size:14px;">import multiprocessing  
    2. import time  
    3.   
    4. def worker_1(ts):  
    5.     print "run worker_1"  
    6.     time.sleep(ts)  
    7.     print "end worker_1"  
    8.   
    9. def worker_2(ts):  
    10.     print "run worker_2"  
    11.     time.sleep(ts)  
    12.     print "end worker_2"  
    13.   
    14. def worker_3(ts):  
    15.     print "run worker_3"  
    16.     time.sleep(ts)  
    17.     print "end worker_3"  
    18.   
    19. def worker_4(ts):  
    20.     print 'run worker_4'  
    21.     time.sleep(ts)  
    22.     print 'end worker_4'  
    23.   
    24. def worker_5(ts):  
    25.     print 'run worker_5'  
    26.     time.sleep(ts)  
    27.     print 'end worker_5'  
    28.   
    29. if __name__ == "__main__":  
    30.     proc1 = multiprocessing.Process(target = worker_1, args = (1,))  
    31.     proc2 = multiprocessing.Process(target = worker_2, args = (2,))  
    32.     proc3 = multiprocessing.Process(target = worker_3, args = (3,))  
    33.     proc4 = multiprocessing.Process(target = worker_4, args = (3,))  
    34.     proc5 = multiprocessing.Process(target = worker_5, args = (3,))  
    35.   
    36.     proc1.start()  
    37.     proc2.start()  
    38.     proc3.start()  
    39.     proc4.start()  
    40.     proc5.start()  
    41.   
    42.     print("The number of CPU is:" + str(multiprocessing.cpu_count()))  
    43.     for p in multiprocessing.active_children():  
    44.         print("child   p.name:" + p.name + " p.id" + str(p.pid))  
    45.     print "main_process finished."</span>  

    运行结果:


    分析:

    通过上面的运行结果可以看到

    (1)在主进程中start启动的5个进程彼此之间以及和主进程均存在并发关系,像上面worker_2在主进程的输出前输出,而且worker1、4、3、5分别无序输出‘run’就是并发最好的说明

    (2)由于worker_1和worker_2分别sleep1秒和2秒,所以在主进程结束后依次结束,而worker_3、worker_4、worker_5都是sleep相同的3秒,最后它们三个进程无序输出(end4、end3、end5)更好的演示了并发效果

    二、将进程写成class的范例

    [python] view plain copy
     
    1. <span style="font-size:14px;">import multiprocessing  
    2. import time  
    3.   
    4. class CounterProcess(multiprocessing.Process):  
    5.     def __init__(self, ts, arr):  
    6.         multiprocessing.Process.__init__(self)  
    7.         self.ts = ts  
    8.         self.arr = arr  
    9.   
    10.     def run(self):  
    11.         time.sleep(self.ts)  
    12.         sum = 0  
    13.         for i in self.arr:  
    14.             sum += i  
    15.         print 'sum = ' + str(sum)  
    16.   
    17.         c_time_cur_loc = time.localtime()  
    18.         counter_timestamp = '%04d%02d%02d_%02d%02d%02d' % (   
    19.             c_time_cur_loc.tm_year,   
    20.             c_time_cur_loc.tm_mon,   
    21.             c_time_cur_loc.tm_mday,   
    22.             c_time_cur_loc.tm_hour,   
    23.             c_time_cur_loc.tm_min,   
    24.             c_time_cur_loc.tm_sec   
    25.             )  
    26.         print 'counter_process finished at ' + str(counter_timestamp)  
    27.   
    28. if __name__ == '__main__':  
    29.     arr = [1, 2, 3, 5, 8, 13, 21, 34, 55, 89]  
    30.     ts = 2  
    31.     counter = CounterProcess(ts, arr)  
    32.     counter.start()  
    33.   
    34.     for i in arr:  
    35.         print 'arr.member = ' + str(i)  
    36.   
    37.     m_time_cur_loc = time.localtime()  
    38.     main_timestamp = '%04d%02d%02d_%02d%02d%02d' % (   
    39.         m_time_cur_loc.tm_year,   
    40.         m_time_cur_loc.tm_mon,   
    41.         m_time_cur_loc.tm_mday,   
    42.         m_time_cur_loc.tm_hour,   
    43.         m_time_cur_loc.tm_min,   
    44.         m_time_cur_loc.tm_sec   
    45.         )  
    46.     print 'main_process finished at ' + str(main_timestamp)</span>  

    运行结果:


    分析:

    这个范例是在主进程中一次输出数组中的斐波那契数列,然后由一个进程counter去计算该数列的累加和。

    其中在进程初始化的时候设置了让该进程sleep两秒,然后在输出的结果中我们也可以看到主进程首先结束,然后在两秒后counter进程完成累加和的运算并且结束(累加和应该不到1ms,直接可以忽略,所以两个进程结束的时间差恰好就是我们预设的2秒)

    三、daemon和join

    (1)daemon:daemon的作用是控制主线程与其他线程的关系,默认情况下daemon=False,也就是当主进程关闭后,在主进程中start出来的进程会继续正常运行,而如果手动设置daemon=True,那么在主进程结束后,从主进程中start的所有其他进程进程也会立刻随着主进程的结束而结束。

    [python] view plain copy
     
    1. <span style="font-size:14px;">import multiprocessing  
    2. import time  
    3.   
    4. class CounterProcess(multiprocessing.Process):  
    5.     def __init__(self, ts, arr):  
    6.         multiprocessing.Process.__init__(self)  
    7.         self.ts = ts  
    8.         self.arr = arr  
    9.   
    10.     def run(self):  
    11.         time.sleep(self.ts)  
    12.         sum = 0  
    13.         for i in self.arr:  
    14.             sum += i  
    15.         print 'sum = ' + str(sum)  
    16.   
    17.         c_time_cur_loc = time.localtime(time.time())  
    18.         counter_timestamp = '%04d%02d%02d_%02d%02d%02d' % (   
    19.             c_time_cur_loc.tm_year,   
    20.             c_time_cur_loc.tm_mon,   
    21.             c_time_cur_loc.tm_mday,   
    22.             c_time_cur_loc.tm_hour,   
    23.             c_time_cur_loc.tm_min,   
    24.             c_time_cur_loc.tm_sec   
    25.             )  
    26.         print 'counter_process finished at ' + str(counter_timestamp)  
    27.   
    28. if __name__ == '__main__':  
    29.     arr = [1, 2, 3, 5, 8, 13, 21, 34, 55, 89]  
    30.     ts = 2  
    31.     counter = CounterProcess(ts, arr)  
    32.     counter.daemon = True  
    33.     counter.start()  
    34.     #counter.join()  
    35.   
    36.   
    37.     for i in arr:  
    38.         print 'arr.member = ' + str(i)  
    39.   
    40.     m_time_cur_loc = time.localtime(time.time())  
    41.     main_timestamp = '%04d%02d%02d_%02d%02d%02d' % (   
    42.         m_time_cur_loc.tm_year,   
    43.         m_time_cur_loc.tm_mon,   
    44.         m_time_cur_loc.tm_mday,   
    45.         m_time_cur_loc.tm_hour,   
    46.         m_time_cur_loc.tm_min,   
    47.         m_time_cur_loc.tm_sec   
    48.         )  
    49.     print 'main_process finished at ' + str(main_timestamp)</span>  

    运行结果:

    分析:

    可以看到,设置了daemon=True后,并没有执行完正在sleep中的counter_process进程,而是随着main_process的结束而终止了。

    (2)join:join的作用是阻塞当前进程,直到调用join的那个进程执行完它的运算,回到当前进程下继续执行当前进程。

    [python] view plain copy
     
    1. <span style="font-size:14px;">import multiprocessing  
    2. import time  
    3.   
    4. class CounterProcess(multiprocessing.Process):  
    5.     def __init__(self, ts, arr):  
    6.         multiprocessing.Process.__init__(self)  
    7.         self.ts = ts  
    8.         self.arr = arr  
    9.   
    10.     def run(self):  
    11.         time.sleep(self.ts)  
    12.         sum = 0  
    13.         for i in self.arr:  
    14.             sum += i  
    15.         print 'sum = ' + str(sum)  
    16.   
    17.         c_time_cur_loc = time.localtime(time.time())  
    18.         counter_timestamp = '%04d%02d%02d_%02d%02d%02d' % (   
    19.             c_time_cur_loc.tm_year,   
    20.             c_time_cur_loc.tm_mon,   
    21.             c_time_cur_loc.tm_mday,   
    22.             c_time_cur_loc.tm_hour,   
    23.             c_time_cur_loc.tm_min,   
    24.             c_time_cur_loc.tm_sec   
    25.             )  
    26.         print 'counter_process finished at ' + str(counter_timestamp)  
    27.   
    28. if __name__ == '__main__':  
    29.     ms_time_cur_loc = time.localtime(time.time())  
    30.     main_s_timestamp = '%04d%02d%02d_%02d%02d%02d' % (   
    31.         ms_time_cur_loc.tm_year,   
    32.         ms_time_cur_loc.tm_mon,   
    33.         ms_time_cur_loc.tm_mday,   
    34.         ms_time_cur_loc.tm_hour,   
    35.         ms_time_cur_loc.tm_min,   
    36.         ms_time_cur_loc.tm_sec   
    37.         )  
    38.     print 'main_process started at ' + str(main_s_timestamp)  
    39.   
    40.     arr = [1, 2, 3, 5, 8, 13, 21, 34, 55, 89]  
    41.     ts = 2  
    42.     counter = CounterProcess(ts, arr)  
    43.     counter.daemon = True  
    44.     counter.start()  
    45.     counter.join()  
    46.   
    47.   
    48.     for i in arr:  
    49.         print 'arr.member = ' + str(i)  
    50.   
    51.     me_time_cur_loc = time.localtime(time.time())  
    52.     main_e_timestamp = '%04d%02d%02d_%02d%02d%02d' % (   
    53.         me_time_cur_loc.tm_year,   
    54.         me_time_cur_loc.tm_mon,   
    55.         me_time_cur_loc.tm_mday,   
    56.         me_time_cur_loc.tm_hour,   
    57.         me_time_cur_loc.tm_min,   
    58.         me_time_cur_loc.tm_sec   
    59.         )  
    60.     print 'main_process finished at ' + str(main_e_timestamp)</span>  

    运行结果:


    分析:

    在本次执行的时候加入了主进程开始执行的时间,然后可以发现当在主进程中join了counter_process之后,就阻塞了当前正在运行的主进程,花了两秒时间完成了counter_process的运算,然后才继续进行main_process的运算,直到结束。

    四、Lock

    既然是并发,一定就会有lock来控制多进程访问共用资源的情况,python中锁有两种状态:被锁(locked)和没有被锁(unlocked),拥有acquire( )和release( )两种方法,并且遵循以下的规则:

    1、unlocked的锁 + acquire( ) = locked的锁

    2、locked的锁 + acquire( ) = 调用acquire( )的进程将进入阻塞,直到其他进程调用release( )方法释放锁

    3、unlocked的锁 + release( ) = 抛出RuntimeError异常

    4、locked的锁 + release( ) = 将该锁的状态由locked转变成unlocked

    感谢yoyzhou提供了一张很清晰的acquire( )和release( )的逻辑图,引用如下所示:

    另外:锁(Lock)可以和"with"语句一起使用,锁可以作为上下文管理器(context manager)。

    使用with的好处是:当程序执行到"with"语句的时候,acquire( )方法将被调用,当程序执行完"with"语句时,release( )方法将被调用。这样我们就不用显示的调用acqiure( )和release( )方法,而是由with语句根据上下文来管理锁的获取和释放。

    [python] view plain copy
     
    1. <span style="font-size:14px;">import multiprocessing  
    2.   
    3. file_strA = 'file_writerA is working'  
    4. file_strB = 'file_writerB is working'  
    5.   
    6. def file_writerA(lock, file_path):  
    7.     print 'file_writerA process started already.'  
    8.     with lock:  
    9.         fs = open(file_path, 'a+')  
    10.         repeat_times = 1000000  
    11.         print 'file_writerA start to write.'  
    12.         while repeat_times >= 1:  
    13.             fs.write(file_strA + ' ')  
    14.             repeat_times -= 1  
    15.         print 'file_writerA finished writing.'  
    16.         fs.close()  
    17.   
    18.   
    19. def file_writerB(lock, file_path):  
    20.     print 'file_writerB process started already.'  
    21.     lock.acquire()  
    22.     try:  
    23.         fs = open(file_path, 'a+')  
    24.         repeat_times = 1000000  
    25.         print 'file_writerB start to write.'  
    26.         while repeat_times >= 1:  
    27.             fs.write(file_strB + ' ')  
    28.             repeat_times -= 1  
    29.         print 'file_writerB finished writing.'  
    30.         fs.close()  
    31.     finally:  
    32.         lock.release()  
    33.   
    34. if __name__ == "__main__":  
    35.     mdr_lock = multiprocessing.Lock()  
    36.     file_path = "E:\file.txt"  
    37.     proc_writerA = multiprocessing.Process(target=file_writerA, args=(mdr_lock, file_path))  
    38.     proc_writerB = multiprocessing.Process(target=file_writerB, args=(mdr_lock, file_path))  
    39.     proc_writerA.start()  
    40.     proc_writerB.start()  
    41.     print "main_process is finished."</span>  

    运行结果:


    分析:

    上面程序中分别使用手动acquire( )/release( )和with两种写法控制两个进程去写相同的文件。

    通过上面的运行结果可以看到,当file_writerA process启动以后,锁住了文件,此时file_writerB process也启动了,但是由于A没有完成写文件,所以B被阻塞,当A完成了写操作以后,B才开始继续执行自己的写文件命令。

    另外需要强调一点,指定相同target的不同进程仍然是不同进程,也会被acquire阻塞住的,如下面实验所示。

    [python] view plain copy
     
    1. <span style="font-size:14px;">import multiprocessing  
    2. import threading  
    3.   
    4. file_strA = 'file_writerA is working'  
    5. file_strB = 'file_writerB is working'  
    6.   
    7. def file_writerA(name, lock, file_path):  
    8.     print 'file_writer process ' + name + ' started already.'  
    9.     print 'inname = ' + multiprocessing.current_process().name  
    10.     with lock:  
    11.         fs = open(file_path, 'a+')  
    12.         repeat_times = 1000000  
    13.         print 'file_writer ' + name + ' start to write.'  
    14.         while repeat_times >= 1:  
    15.             fs.write(file_strA + ' ')  
    16.             repeat_times -= 1  
    17.         print 'file_writer ' + name + ' finished writing.'  
    18.         fs.close()  
    19.   
    20. def file_writerB(name, lock, file_path):  
    21.     print 'file_writer process ' + name + ' started already.'  
    22.     print 'inname = ' + multiprocessing.current_process().name  
    23.     lock.acquire()  
    24.     try:  
    25.         fs = open(file_path, 'a+')  
    26.         repeat_times = 1000000  
    27.         print 'file_writer ' + name + ' start to write.'  
    28.         while repeat_times >= 1:  
    29.             fs.write(file_strB + ' ')  
    30.             repeat_times -= 1  
    31.         print 'file_writer ' + name + ' finished writing.'  
    32.         fs.close()  
    33.     finally:  
    34.         lock.release()  
    35.   
    36. if __name__ == "__main__":  
    37.     mdr_lock = multiprocessing.Lock()  
    38.     file_path = "E:\file.txt"  
    39.     proc_writerA1 = multiprocessing.Process(target=file_writerA, args=('A1', mdr_lock, file_path,))  
    40.     proc_writerA2 = multiprocessing.Process(target=file_writerA, args=('A2', mdr_lock, file_path,))  
    41.     proc_writerB = multiprocessing.Process(target=file_writerB, args=('B', mdr_lock, file_path,))  
    42.     proc_writerA1.start()  
    43.     proc_writerA2.start()  
    44.     proc_writerB.start()  
    45.     print 'main_process is finished.'  
    46. </span>  

    运行结果:


    分析:

    上面的实验可以看到,proc_writerA1和pro_writerA2都是指定targer=file_writerA,但是从运行结果上我们看到A2被阻塞到A1和B都执行完毕才开始执行自己的写操作。

    五、RLock

    RLock是可重入锁(reetrant lock),和Lock对比:

    (1)相同之处:当某一个进程lock.acquire( )后,直到其释放前,其他所有acquire相同lock的进程将被阻塞(包括自身进程)。

    (2)区别之处:是同一个进程能够不被阻塞的多次调用rlock.acquire( ),同样需要相等次数的release( )才能释放后,其他进程才可以结束acquire的阻塞。

    参考文献:

    http://www.cnblogs.com/kaituorensheng/p/4445418.html

    http://www.cnblogs.com/lipijin/p/3709903.html

    http://yoyzhou.github.io/blog/2013/02/28/python-threads-synchronization-locks/

  • 相关阅读:
    GreenPlum 锁表以及解除锁定
    Postgresql 解决锁表
    Greenplum 查看连接与锁信息数据字典
    Greenplum 常用数据字典
    Linux 内核参数说明
    Greenplum 如何直连segment节点
    GreenPlum 数据备份与恢复
    unity 获取DontDestroyOnLoad的游戏对象
    scheduleOnce时出错,CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: 0 to 0"
    正比适配,留黑边
  • 原文地址:https://www.cnblogs.com/ExMan/p/9052279.html
Copyright © 2020-2023  润新知