文章目录

    • 多进程实践——multiprocessing
    • . 延伸一:Caffe Python接口多进程提取特征
  • 多线程案例——threading
    • 1、普通的threading
    • 4.线程锁与线程同步
    • 5 threading与Class一起用,很好用
    • python 性能调试工具(line_profiler)
  • python通过tqdm 执行时间
      • 安装
      • 在迭代器for中使用:
      • trange的方式:
      • 当迭代的内容为list:
      • 手动的控制更新
    • 函数执行时间函数两个小技巧:
    • 延伸二:Python 多进程实践

多进程实践——multiprocessing

笔者最近在实践多进程发现multiprocessing,真心很好用,不仅加速了运算,同时可以GPU调用,而且互相之间无关联,这样可以很放心的进行计算。

譬如(参考:多进程):

from multiprocessing import Pool
import os, time, randomdef long_time_task(name):print 'Run task %s (%s)...' % (name, os.getpid())start = time.time()time.sleep(random.random() * 3)end = time.time()print 'Task %s runs %0.2f seconds.' % (name, (end - start))if __name__=='__main__':print 'Parent process %s.' % os.getpid()p = Pool()for i in range(5):p.apply_async(long_time_task, args=(i,))print 'Waiting for all subprocesses done...'p.close()p.join()print 'All subprocesses done.'

先载入multiprocessing 模块Pool,
然后定义一个函数long_time_task;
创建一个进程池: p = Pool(),
for i in range(5):即为定义开一个进程,此处发现ubuntu里面用spyder中的ipython,开多进程CPU时候,只能开到4个(可能默认开到4个内存占满了);
args是long_time_task函数的参数项,
一定要p.close()之后才能执行后续内容,
然后用p.join()调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了。

请注意输出的结果,task 0,1,2,3是立刻执行的,而task 4要等待前面某个task完成后才执行,这是因为Pool的默认大小在我的电脑上是4,因此,最多同时执行4个进程。这是Pool有意设计的限制,并不是操作系统的限制。如果改成:

p = Pool(5)

就可以同时跑5个进程。
由于Pool的默认大小是CPU的核数,如果你不幸拥有8核CPU,你要提交至少9个子进程才能看到上面的等待效果。

multiprocessing在window运行的问题

在window运行下,笔者自己尝试的时候,一直搞不定:

from multiprocessing import Process, Queue
from multiprocessing import Process, freeze_support
import os# 将需要进程处理的代码放进这个函数
def while_process_run(queue, name):print('Process {} (pid:{}) starting...'.format(name, os.getpid()))queue.put('return结果')
# 这个函数不能用return来返回值(无法得到这个值)
# 实现类似return值的效果要借助一个传入的参数
# 这个参数就是用Queue()创建的一个变量freeze_support()
q = Queue()
p1 = Process(target=while_process_run, args=(q, 'test',))
p1.start()
p1.join()
print('process ended.')
print(q.get())
因为 Windows 缺乏 Linux 那种 fork, 所以它会有一些额外的限制:不管是绑定还是未绑定的方法, 都不要直接作为参数传给 Process 初始化的 target, 相反应该要用普通的函数代替
子进程在访问全局变量时, 可能会与父进程的值不同。 ( 模块级别的常量没这问题 )
开启新Python 解析器或者创建新 process 时, 确定主模块能够安全的导入。

来源:
https://zhuanlan.zhihu.com/p/42484984

.
延伸一:Caffe Python接口多进程提取特征

那么在做图像处理的时候,进行预测任务的时候,可以开多进程,GPU方案。那么步骤是:

  • 1、分割数据;
  • 2、多个进程池。

第一步:分割数据,用split_list函数:

def split_list(alist, wanted_parts=1):length = len(alist)return [ alist[i*length // wanted_parts: (i+1)*length // wanted_parts] for i in range(wanted_parts) ]

第二步:开多个进程池
可参考博客:机器视觉:Caffe Python接口多进程提取特征

.


多线程案例——threading

1、普通的threading

参考:python 并发执行之多线程

import threading
import time
def haha(max_num):"""随便定义一个函数,要求用户输入一个要打印数字的最大范围输入之后就会从0开始打印,直到用户输入的最大范围"""for i in range(max_num):"""每次打印一个数字要间隔1秒,那么打印10个数就要耗时10秒"""time.sleep(1)print i
for x in range(3):"""这里的rang(3)是要依次启动三个线程,每个线程都调用函数haha()第一个线程启动执行之后,马上启动第二个线程再次执行。最后也相当函数执行了3次"""#通过threading.Thread方法实例化多线程类#target后面跟的是函数的名称但是不要带括号也不填写参数#args后面的内容才是要传递给函数haha()的参数。切记参数一定要以数组的形式填写不然会报错。t=threading.Thread(target=haha,args=(10,))#将线程设置为守护线程t.setDaemon(True)#线程准备就绪,随时等候cpu调度t.start()

其中setDaemon 这个参数是True,就表示程序流程跑完之后直接就关闭线程然后退出了,根本不管线程是否执行完。
.

  1. join()

结果看起来规则一些可以考虑使用join()方法,参考:python 并发执行之多线程
join(timeout)方法将会等待直到线程结束。这将阻塞正在调用的线程,直到被调用join()方法的线程结束。

import threading
import time
def haha(max_num):for i in range(max_num):time.sleep(1)print i
for x in range(3):t=threading.Thread(target=haha,args=(5,))t.start()#通过join方法让线程逐条执行t.join()
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
  1. 多线程循环

背景:Python脚本:读取文件中每行,放入列表中;循环读取列表中的每个元素,并做处理操作。
核心:多线程处理单个for循环函数调用

#!/usr/bin/env python
#-*- coding: utf8 -*-import sys
import time
import string
import threading
import datetime
fileinfo = sys.argv[1]# 读取文件内容放入列表
host_list = []
port_list = []# 定义函数:读取文件内容放入列表中
def CreateList():f = file(fileinfo,'r')for line in f.readlines():host_list.append(line.split(' ')[0])port_list.append(line.split(' ')[1])return host_listreturn port_listf.close()# 单线程 循环函数,注释掉了
#def CreateInfo():
#    for i in range(0,len(host_list)):     # 单线程:直接循环列表
#        time.sleep(1)
#        TimeMark = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
#        print "The Server's HostName is %-15s and Port is %-4d !!! [%s]" % (host_list[i],int(port_list[i]),TimeMark)
#   #  定义多线程循环调用函数
def MainRange(start,stop):     #提供列表index起始位置参数for i in range(start,stop):time.sleep(1)TimeMark = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')print "The Server's HostName is %-15s and Port is %-4d !!! [%s]" % (host_list[i],int(port_list[i]),TimeMark)# 执行函数,生成列表
CreateList()
# 列表分割成:两部分 mid为列表的index中间位置
mid = int(len(host_list)/2)# 多线程部分
threads = []
t1 = threading.Thread(target=MainRange,args=(0,mid))
threads.append(t1)
t2 = threading.Thread(target=MainRange,args=(mid,len(host_list)))
threads.append(t2)for t in threads:t.setDaemon(True)t.start()
t.join()
print "ok"

也有一个分拆的步骤,args=(0,mid),args=(mid,len(host_list)

.

4.线程锁与线程同步

当你有多个线程,就需要考虑怎样避免线程冲突。解决办法就是使用线程锁。锁由 Python 的 threading 模块提供,并且它最多被一个线程所持有。当一个线程试图获取一个已经锁在资源上的锁时,该线程通常会暂停运行,直到这个锁被释放。
让我们给这个函数添加锁。有两种方法可以实现。第一种方式是使用 try/finally ,从而确保锁肯定会被释放。下面是示例:

import threadingtotal = 0
lock = threading.Lock()def update_total(amount):"""Updates the total by the given amount"""global totallock.acquire()try:total += amountfinally:lock.release()print (total)if __name__ == '__main__':for i in range(10):my_thread = threading.Thread(target=update_total, args=(5,))my_thread.start()

由 with 语句作为替代。

import threadingtotal = 0
lock = threading.Lock()def do_something():lock.acquire()try:print('Lock acquired in the do_something function')finally:lock.release()print('Lock released in the do_something function')return "Done doing something"def do_something_else():lock.acquire()try:print('Lock acquired in the do_something_else function')finally:lock.release()print('Lock released in the do_something_else function')return "Finished something else"if __name__ == '__main__':result_one = do_something()result_two = do_something_else()

可重入锁
为了支持在同一线程中多次请求同一资源,python提供了可重入锁(RLock)。RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。

即把 lock = threading.lock() 替换为 lock = threading.RLock(),然后重新运行代码,现在代码就可以正常运行了。

参考文献:
Python 多线程
一文学会 Python 多线程编程

5 threading与Class一起用,很好用

from threading import Thread
from time import sleep, ctime# 创建 Thread 的子类
class MyThread(Thread):def __init__(self, num):''':param func: 可调用的对象:param args: 可调用对象的参数'''Thread.__init__(self)self.num = numself.start()def run(self):# 创建 Thread 实例print(self.num)if __name__ == '__main__':for i in [1,2,3,4,45,5]:MyThread(i)1
2
3
4
455

而且可以一次性运行,书写结构也非常简单

.


python 性能调试工具(line_profiler)

参考:python 性能调试工具(line_profiler)使用
网上大部分都是说在所需要测的函数前面加一个@profile,如文档所说。但是加了@profile后函数无法直接运行,只能优化的时候加上,调试的时候又得去掉。文章中提到了这个问题的解决办法,个人觉得还是有点麻烦,不太能理解这是为什么。我在stackoverflow上看到了另一种关于line_profile的使用方法,简单而且实用。


from line_profiler import LineProfiler
import randomdef do_stuff(numbers):s = sum(numbers)l = [numbers[i]/43 for i in range(len(numbers))]m = ['hello'+str(numbers[i]) for i in range(len(numbers))]numbers = [random.randint(1,100) for i in range(1000)]
lp = LineProfiler()
lp_wrapper = lp(do_stuff)
lp_wrapper(numbers)
lp.print_stats()

输出结果:

Timer unit: 1e-06 sTotal time: 0.000649 s
File: <ipython-input-2-2e060b054fea>
Function: do_stuff at line 4Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================4                                           def do_stuff(numbers):5         1           10     10.0      1.5      s = sum(numbers)6         1          186    186.0     28.7      l = [numbers[i]/43 for i in range(len(numbers))]7         1          453    453.0     69.8      m = ['hello'+str(numbers[i]) for i in range(len(numbers))]

.


python通过tqdm 执行时间

来源:python tqdm模块分析

安装

pip install tqdm

在迭代器for中使用:

from tqdm import tqdm
for i in tqdm(range(9)):...

同时也可以支持这样的迭代方式:

[i for i in tqdm(range(9))]

trange的方式:

>>> for i in trange(100):
...     sleep(0.1)
100%|################################################################| 100/100 [00:10<00:00,  9.97it/s]

当迭代的内容为list:

>>> pbar = tqdm(["a", "b", "c", "d"])
>>> for char in pbar:
...         pbar.set_description("Processing %s" % char)
Processing d: 100%|######################################################| 4/4 [00:06<00:00,  1.53s/it]

手动的控制更新

把运行的粒度放宽


>>> with tqdm(total=100) as pbar:
...     for i in range(10):
...         sleep(0.1)
...         pbar.update(10)
100%|################################################################| 100/100 [00:01<00:00, 99.60it/s]

函数执行时间函数两个小技巧:

# 第一种方式
%time [i+1 for i in range(100)]# 第二种方式
%%time
[i+1 for i in range(100)]

返回的结果都是:

CPU times: user 0 ns, sys: 0 ns, total: 0 ns
Wall time: 26.9 µs

延伸二:Python 多进程实践

参考:Python 多进程实践
多进程的方式可以增加脚本的并发处理能力, python 支持这种多进程的编程方式
在类unix系统中, python的os 模块内置了fork 函数用以创建子进程

1、fork 方式创建子进程

从结果可以看到, 从pid = os.fork() 开始, 下面的部分代码运行了两次, 第一次是父进程运行, 第二次是子进程运行, 且子进程的fork的结果总是0, 所以这个也可以用来作为区分父进程或是子进程标志

那么变量在多个进程之间是否相互影响呢
import os

很明显, 初始值为10的source 在父进程中值 减少了 1, 为9, 而子进程明显source的初始值 是10, 也就是说多进程之间并没有什么相互影响

python︱Python进程、线程、协程详解、运行性能、效率(tqdm)相关推荐

  1. Python之进程+线程+协程(异步、selectors模块、阻塞、非阻塞IO)

    文章目录 一.IO多路复用 二.selectors模块 本篇文字是关于IO多路复用的更深入一步的总结,上一篇 Python之进程+线程+协程(事件驱动模型.IO多路复用.select与epoll)对I ...

  2. 【转载】Python线程、进程和协程详解

    从操作系统角度 操作系统处理任务,调度单位是进程和线程. 进程:表示一个程序的执行活动(打开程序.读写程序数据.关闭程序) 线程:执行某个程序时,该进程调度的最小执行单位(执行功能1,执行功能2) 一 ...

  3. Python之进程+线程+协程(并发与并行、GIL锁、同步锁、死锁、递归锁)

    文章目录 一.并发与并行 二.同步与异步 三.线程锁 1.GIL全局解释器锁 2.同步锁 3.死锁 4.递归锁 在Python中GIL解释器锁.同步锁.死锁.递归锁都是什么?怎么这么多锁,它们都是用来 ...

  4. Python之进程+线程+协程(进程的本质 与 threading线程模块)

    文章目录 基本概念 threading线程模块 本篇开始分析Python中的并发程序,也就是进程.线程.协程序的使用.由于是用自己的语言总结的,因此比较大白话,或者叫通俗易懂.而且很多细节方面没有具体 ...

  5. Python之进程+线程+协程(事件驱动模型、IO多路复用、select与epoll)

    文章目录 一.事件驱动模型 二.IO多路复用 本篇文章是关于涉及网络编程与协程.进程之间结合的内容,其中事件驱动模型.IO多路复用.select与epoll的使用等方面的知识 一.事件驱动模型 1.事 ...

  6. Python之进程+线程+协程(multiprocessing多进程模块)

    前几篇的多线程模块的各种规则和用法,本篇则是关于多进程模块的内容 1.multiprocessing的介绍 在Python中,由于有GIL解释器锁的存在,多线程就根本不是本质意义上的多线程,而是一个主 ...

  7. Python之进程+线程+协程(同步对象、信号量、队列)

    文章目录 Event同步对象 semaphore信号量 队列 本篇是关于Python进程方面的内容了,主要是Event同步对象,信号量和队列 Event同步对象 1.概念: 我们可以对一个线程set一 ...

  8. Python之进程+线程+协程(进程间通信、进程同步、进程池、回调函数)

    文章目录 进程间通信 进程同步 进程池 回调函数 本篇文章依然是进程.线程方面的内容,主要讲进程间的通信.进程队列.进程同步.进程池.进程同步和回调函数 进程间通信 进程就是两个独立的内存空间在运行, ...

  9. Python之进程+线程+协程(生产者消费者模型)

    本篇主要总结一下非常有名的生成者消费者模型 概念引用 1.为什么要使用生产者和消费者模型? 在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程.在多线程开发当中,如果生产者处理速度很快, ...

  10. python协程详解

    目录 python协程详解 一.什么是协程 二.了解协程的过程 1.yield工作原理 2.预激协程的装饰器 3.终止协程和异常处理 4.让协程返回值 5.yield from的使用 6.yield ...

最新文章

  1. DataBinder.Eval()方法绑定数据
  2. VR可以用做除游戏外的哪些地方
  3. 有关数据库的多库查询
  4. cp命令复制目录 不覆盖
  5. jvm_虚拟机参数讲解(二)
  6. [leetcode] 103.二叉树的锯齿形遍历
  7. Win32ASM学习[20]:子程序
  8. ELK+Kafka部署
  9. 计算机二,八,十,十六进制转换
  10. 平均薪资 38.4 万!3 步教你成为区块链开发者,收好这份学习指南!
  11. appinventor贪吃蛇制作步骤_旋转RGB制作指导
  12. AngularJS scope 作用域的问题
  13. Windows Security Center注册分析
  14. 【代码质量】C/C++代码静态分析与常用分析软件工具
  15. 知识图谱构建(概念,工具,实例调研)
  16. 常见色彩表(RGB)
  17. java17长期支持版本_Java 8后的首个长期支持版本Java 11
  18. 计算机网络16进制地址,计算机内存地址和为什么用16进制?
  19. BH1750光强度传感器Stm32f103驱动(已测试ok)
  20. axure sketch 对比_Sketch to Axure RP插件下载

热门文章

  1. python登录验证码生成及自动化测试规避
  2. eclipse maven 插件的安装和配置
  3. 集合源码(一)之hashMap、ArrayList
  4. 合伙人和创始人的区别
  5. 华硕P4P800-X 主板南桥芯片又烧坏了......
  6. SQL Server数据库事务处理详解(MSDN网上资源)
  7. 实习程序员工资2.5K,公司团建还要自己掏钱:领导以为你爱吃泡面
  8. 演示如何利用log4net记录程序日志信息
  9. memcached+magent实现负载
  10. 从零开始学习搜索引擎(开篇)---Searcharoo.net