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

一、多进程并发效果演示

  1. <span style="font-size:14px;">import multiprocessing
  2. import time
  3. def worker_1(ts):
  4. print "run worker_1"
  5. time.sleep(ts)
  6. print "end worker_1"
  7. def worker_2(ts):
  8. print "run worker_2"
  9. time.sleep(ts)
  10. print "end worker_2"
  11. def worker_3(ts):
  12. print "run worker_3"
  13. time.sleep(ts)
  14. print "end worker_3"
  15. def worker_4(ts):
  16. print 'run worker_4'
  17. time.sleep(ts)
  18. print 'end worker_4'
  19. def worker_5(ts):
  20. print 'run worker_5'
  21. time.sleep(ts)
  22. print 'end worker_5'
  23. if __name__ == "__main__":
  24. proc1 = multiprocessing.Process(target = worker_1, args = (1,))
  25. proc2 = multiprocessing.Process(target = worker_2, args = (2,))
  26. proc3 = multiprocessing.Process(target = worker_3, args = (3,))
  27. proc4 = multiprocessing.Process(target = worker_4, args = (3,))
  28. proc5 = multiprocessing.Process(target = worker_5, args = (3,))
  29. proc1.start()
  30. proc2.start()
  31. proc3.start()
  32. proc4.start()
  33. proc5.start()
  34. print("The number of CPU is:" + str(multiprocessing.cpu_count()))
  35. for p in multiprocessing.active_children():
  36. print("child   p.name:" + p.name + "\tp.id" + str(p.pid))
  37. 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的范例

  1. <span style="font-size:14px;">import multiprocessing
  2. import time
  3. class CounterProcess(multiprocessing.Process):
  4. def __init__(self, ts, arr):
  5. multiprocessing.Process.__init__(self)
  6. self.ts = ts
  7. self.arr = arr
  8. def run(self):
  9. time.sleep(self.ts)
  10. sum = 0
  11. for i in self.arr:
  12. sum += i
  13. print 'sum = ' + str(sum)
  14. c_time_cur_loc = time.localtime()
  15. counter_timestamp = '%04d%02d%02d_%02d%02d%02d' % ( \
  16. c_time_cur_loc.tm_year, \
  17. c_time_cur_loc.tm_mon, \
  18. c_time_cur_loc.tm_mday, \
  19. c_time_cur_loc.tm_hour, \
  20. c_time_cur_loc.tm_min, \
  21. c_time_cur_loc.tm_sec \
  22. )
  23. print 'counter_process finished at ' + str(counter_timestamp)
  24. if __name__ == '__main__':
  25. arr = [1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
  26. ts = 2
  27. counter = CounterProcess(ts, arr)
  28. counter.start()
  29. for i in arr:
  30. print 'arr.member = ' + str(i)
  31. m_time_cur_loc = time.localtime()
  32. main_timestamp = '%04d%02d%02d_%02d%02d%02d' % ( \
  33. m_time_cur_loc.tm_year, \
  34. m_time_cur_loc.tm_mon, \
  35. m_time_cur_loc.tm_mday, \
  36. m_time_cur_loc.tm_hour, \
  37. m_time_cur_loc.tm_min, \
  38. m_time_cur_loc.tm_sec \
  39. )
  40. 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的所有其他进程进程也会立刻随着主进程的结束而结束。

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

运行结果:

分析:

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

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

  1. <span style="font-size:14px;">import multiprocessing
  2. import time
  3. class CounterProcess(multiprocessing.Process):
  4. def __init__(self, ts, arr):
  5. multiprocessing.Process.__init__(self)
  6. self.ts = ts
  7. self.arr = arr
  8. def run(self):
  9. time.sleep(self.ts)
  10. sum = 0
  11. for i in self.arr:
  12. sum += i
  13. print 'sum = ' + str(sum)
  14. c_time_cur_loc = time.localtime(time.time())
  15. counter_timestamp = '%04d%02d%02d_%02d%02d%02d' % ( \
  16. c_time_cur_loc.tm_year, \
  17. c_time_cur_loc.tm_mon, \
  18. c_time_cur_loc.tm_mday, \
  19. c_time_cur_loc.tm_hour, \
  20. c_time_cur_loc.tm_min, \
  21. c_time_cur_loc.tm_sec \
  22. )
  23. print 'counter_process finished at ' + str(counter_timestamp)
  24. if __name__ == '__main__':
  25. ms_time_cur_loc = time.localtime(time.time())
  26. main_s_timestamp = '%04d%02d%02d_%02d%02d%02d' % ( \
  27. ms_time_cur_loc.tm_year, \
  28. ms_time_cur_loc.tm_mon, \
  29. ms_time_cur_loc.tm_mday, \
  30. ms_time_cur_loc.tm_hour, \
  31. ms_time_cur_loc.tm_min, \
  32. ms_time_cur_loc.tm_sec \
  33. )
  34. print 'main_process started at ' + str(main_s_timestamp)
  35. arr = [1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
  36. ts = 2
  37. counter = CounterProcess(ts, arr)
  38. counter.daemon = True
  39. counter.start()
  40. counter.join()
  41. for i in arr:
  42. print 'arr.member = ' + str(i)
  43. me_time_cur_loc = time.localtime(time.time())
  44. main_e_timestamp = '%04d%02d%02d_%02d%02d%02d' % ( \
  45. me_time_cur_loc.tm_year, \
  46. me_time_cur_loc.tm_mon, \
  47. me_time_cur_loc.tm_mday, \
  48. me_time_cur_loc.tm_hour, \
  49. me_time_cur_loc.tm_min, \
  50. me_time_cur_loc.tm_sec \
  51. )
  52. 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语句根据上下文来管理锁的获取和释放。

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

运行结果:


分析:

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

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

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

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

python基础:multiprocessing的使用的更多相关文章

  1. python基础教程

    转自:http://www.cnblogs.com/vamei/archive/2012/09/13/2682778.html Python快速教程 作者:Vamei 出处:http://www.cn ...

  2. 周末班:Python基础之并发编程

    进程 相关概念 进程 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础.在早期面向进程设计的计算机结构中,进程是程序的基本 ...

  3. 面试题-python基础

    一.Python基础 1.什么是python?使用python有什么好处? python是一种编程语言,它有对象.模块.线程.异常处理和自动内存管理.它简洁,简单.方便.容易扩展.有许多自带的数据结果 ...

  4. python基础系列教程——Python3.x标准模块库目录

    python基础系列教程——Python3.x标准模块库目录 文本 string:通用字符串操作 re:正则表达式操作 difflib:差异计算工具 textwrap:文本填充 unicodedata ...

  5. Python 基础之 线程与进程

    Python 基础之 线程与进程 在前面已经接触过了,socket编程的基础知识,也通过socketserver 模块实现了并发,也就是多个客户端可以给服务器端发送消息,那接下来还有个问题,如何用多线 ...

  6. python基础31[常用模块介绍]

    python基础31[常用模块介绍]   python除了关键字(keywords)和内置的类型和函数(builtins),更多的功能是通过libraries(即modules)来提供的. 常用的li ...

  7. python基础全部知识点整理,超级全(20万字+)

    目录 Python编程语言简介 https://www.cnblogs.com/hany-postq473111315/p/12256134.html Python环境搭建及中文编码 https:// ...

  8. python之最强王者(2)——python基础语法

    背景介绍:由于本人一直做java开发,也是从txt开始写hello,world,使用javac命令编译,一直到使用myeclipse,其中的道理和辛酸都懂(请容许我擦干眼角的泪水),所以对于pytho ...

  9. Python开发【第二篇】:Python基础知识

    Python基础知识 一.初识基本数据类型 类型: int(整型) 在32位机器上,整数的位数为32位,取值范围为-2**31-2**31-1,即-2147483648-2147483647 在64位 ...

  10. Python小白的发展之路之Python基础(一)

    Python基础部分1: 1.Python简介 2.Python 2 or 3,两者的主要区别 3.Python解释器 4.安装Python 5.第一个Python程序 Hello World 6.P ...

随机推荐

  1. aria2的安装与配置

    aria2安装 安装 epel 源: yum install epel-release 然后直接安装: yum install aria2 -y 配置 Aria2 创建目录与配置文件 这一步需要切换到 ...

  2. CentOS7安装并使用Ceph

    1.准备工作1.1 安装配置NTP官方建议在所有 Ceph 节点上安装 NTP 服务(特别是 Ceph Monitor 节点),以免因时钟漂移导致故障. ln -sf /usr/share/zonei ...

  3. Spring MVC文件上传下载(转载)

    原文地址: http://www.cnblogs.com/WJ-163/p/6269409.html 上传参考 http://www.cnblogs.com/lonecloud/p/5990060.h ...

  4. svn 服务器的搭建以及客户端的使用

    1.svn 服务器的搭建以及客户端的使用,安装见下面的博客 https://blog.csdn.net/zh123456zh789/article/details/80921179 说明:服务器只是用 ...

  5. python之路day15--内置函数

    函数分为自定义函数和内置函数 python内置函数分类: 5.5.1 强制转换 int() / str() / bool() / list() / tuple() / dict() / set() 5 ...

  6. NOIP2016提高A组模拟10.15总结

    第一题,就是将原有的式子一步步简化,不过有点麻烦,搞了很久. 第二题,枚举上下边界,维护一个单调队列,二分. 比赛上没有想到,只打了个暴力,坑了80分. 第三题,贪心,最后的十多分钟才想到,没有打出来 ...

  7. Ubuntu18.04下更改apt源为阿里云源

    1.复制源文件备份,以防万一 我们要修改的文件是sources.list,它在目录/etc/apt/下,sources.list是包管理工具apt所用的记录软件包仓库位置的配置文件,同样类型的还有位于 ...

  8. sql 查询 某字段是否重复

    select count(*) from ( select * from 客户 )C GROUP BY 客户编码 select * from ( select count(*)num from ( s ...

  9. Linux 系统磁盘空间占满,df 和 du 结果不一致

    服务器运行一段时间后df查看磁盘剩余空间不足,通过du统计发现被几个文件占用,遂删除之.过了一段时间磁盘空间再次不足,通过du统计却找不到那么多大文件.搜索后才得知原因:文件删除后空间没有释放,du统 ...

  10. Kotlin使用率达35%,Java要退位了?

    在今年的Google I/O大会上,关于Kotlin,Google只说了只言片语: 在过去一年里,有35%的专业Android开发者在使用Kotlin,其中95%的开发者都对Kotlin非常满意. 之 ...