Python—进程、线程、协程
一、线程
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务
方法:
start 线程准备就绪,等待CPU调度
setName 设置线程名称
getName 获取线程名称
setDaemon 把一个主进程设置为Daemon线程后,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论有没执行完成,都会停止
join 逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义
run 线程被cpu调度后自动执行线程对象的run方法
threading模块
线程的两种调用方式:
1.直接调用(常用)
import threading
import time '''直接调用''' def hello(name):
print("Hello %s"%name)
time.sleep(3) if __name__ == "__main__":
t1=threading.Thread(target=hello,args=("zhangsan",)) #生成线程实例
t2=threading.Thread(target=hello,args=("lisi",)) t1.setName("aaa") #设置线程名
t1.start() #启动线程
t2.start()
t2.join() #join 等待t2先执行完
print("Hello")
print(t1.getName()) #获取线程名
2.继承式调用
'''继承式调用'''
import threading
import time
class MyThread(threading.Thread):
def __init__(self,name):
threading.Thread.__init__(self)
self.name = name
def run(self):
print("Hello %s"%self.name)
time.sleep(3) if __name__ == "__main__":
t1=MyThread("zhangsan")
t2=MyThread("lisi")
t1.start()
t2.start()
setDaemon线程
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import time
import threading
def run(n):
print('Hello..[%s]\n' % n)
time.sleep(2) def main():
for i in range(5):
t = threading.Thread(target=run,args=[i,])
t.start()
t.join(1) m = threading.Thread(target=main,args=[])
m.setDaemon(True) #将主线程设置Daemon设置为True后,主线程执行完成时,其它子线程会同时退出,不管是否执行完任务
m.start()
print("--- done----")
线程锁Lock
一个进程下可以启动多个线程,多个线程共享父进程的内存空间,每个线程可以访问同一份数据,所以当多个线程同时要修改同一份数据时,就会出现错误
例如:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import threading
import time num = 100 #设置一个共享变量
def show():
global num #在函数内操作函数外变量,需设置为全局变量
time.sleep(1)
num -= 1
list=[]
for i in range(100):
t = threading.Thread(target=show)
t.start()
list.append(t) for t in list:
t.join()
print(num)
上面的例子在正常执行完成后的num的结果应该是0,但实际上每次的执行结果都不太一样,因为当多个线程同时要修改同一份数据时,就会出现一些错误(只有
在python2.x运行才会出现错误,python3.x中不会),所以每个线程在要修改公共数据时,为了避免自己在还没改完的时候别人也来修改此数据,可以加上线程锁
来确保每次修改数据时只有一个线程在操作。
加锁代码
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import threading
import time num = 100 #设置一个共享变量
lock=threading.Lock() #生成全局锁
def show():
global num #在函数内操作函数外变量,需设置为全局变量
time.sleep(1)
lock.acquire() #修改前加锁
num -= 1
lock.release() #修改后解锁
list=[]
for i in range(100):
t = threading.Thread(target=show)
t.start()
list.append(t) for t in list:
t.join() print(num)
递归锁RLock
就是在一个大锁中再包含子锁
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import threading
#递归锁
def run1():
lock.acquire() #小锁
global num
num +=1
lock.release()
return num
def run2():
lock.acquire() #小锁
global num2
num2+=1
lock.release()
return num2
def run3():
lock.acquire() #大锁
res = run1()
res2 = run2()
lock.release()
print(res,res2) if __name__ == '__main__':
num,num2 = 0,0
lock = threading.RLock() #生成Rlock
for i in range(10):
t = threading.Thread(target=run3)
t.start() while threading.active_count() != 1:#如果不等于1,说明子线程还没执行完毕
pass #打印进程数
else:
print(num,num2)
Semaphore
同时允许一定数量的线程更改数据
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import threading
import time
def run(n):
semaphore.acquire()
time.sleep(1)
print("run the thread: %s" %n)
semaphore.release() if __name__ == '__main__':
semaphore = threading.BoundedSemaphore(3) #设置最多允许3个线程同时运行
for i in range(20):
t = threading.Thread(target=run,args=(i,))
t.start()
while threading.active_count() != 1:
pass
else:
print('----done---')
event
实现两个或多个线程间的交互,提供了三个方法 set、wait、clear,默认碰到event.wait 方法时就会阻塞。
event.set(),设定后遇到wait不阻塞
event.clear(),设定后遇到wait后阻塞
event.isSet(),判断有没有被设定
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import threading
def start():
print("---start---1")
event.wait() #阻塞
print("---start---2") if __name__ == "__main__":
event = threading.Event()
t = threading.Thread(target=start)
t.start() result=input(">>:")
if result == "set":
event.set() #设定set,wait不阻塞
二、进程
multiprocessing模块
进程调用
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from multiprocessing import Process
import time
def start(name):
time.sleep(1)
print('hello', name) if __name__ == '__main__':
p = Process(target=start, args=('zhangsan',))
p1 = Process(target=start, args=('lisi',))
p.start()
p1.start()
p.join()
进程间通讯
每个进程都拥有自己的内存空间,因此不同进程间内存是不共享的,要想实现两个进程间的数据交换,有几种方法
Queue(队列)
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from multiprocessing import Process, Queue
def start(q):
q.put( 'hello') if __name__ == '__main__':
q = Queue()
p = Process(target=start, args=(q,))
p.start()
print(q.get())
p.join()
Pipe(管道,不常用)
把管道的两头分别赋给两个进程,实现两个进程的互相通信
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from multiprocessing import Process, Pipe def start(conn):
conn.send('hello')#发送
print(conn.recv())#接收
conn.close() if __name__ == '__main__':
parent_conn, child_conn = Pipe() #生成一个管道
p = Process(target=start, args=(child_conn,))
p.start()
print(parent_conn.recv())#接收
parent_conn.send("")#发送
p.join()
Manager(实现了进程间真正的数据共享)
#!/usr/bin/env python
from multiprocessing import Process, Manager
def f(dic, list,i):
dic[''] = 1
dic[''] = 2
dic[''] = 3
list.append(i) if __name__ == '__main__':
manager = Manager()
dic = manager.dict()#通过manager生成一个字典
list = manager.list(range(5))#通过manager生成一个列表
p_list = []
for i in range(10):
p = Process(target=f, args=(dic, list,i))
p.start()
p_list.append(p)
for res in p_list:
res.join() print(dic)
print(list)
#执行结果
'''
{'2': 2, '3': 3, '1': 1}
[0, 1, 2, 3, 4, 1, 9, 2, 5, 3, 7, 6, 0, 8, 4]
'''
进程池
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进程,那么程序就会等待,直到进程池中有可用进程为止。
进程池中有两个方法:
1、apply(同步)
2、apply_async(异步)
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from multiprocessing import Process,Pool
import time def Foo(i):
time.sleep(1)
return i+100 def Bar(arg):
print('number::',arg) if __name__ == "__main__":
pool = Pool(3)#定义一个进程池,里面有3个进程
for i in range(10):
pool.apply_async(func=Foo, args=(i,),callback=Bar)
#pool.apply(func=Foo, args=(i,)) pool.close()#关闭进程池
pool.join()#进程池中进程执行完毕后再关闭,(必须先close在join)
callback是回调函数,就是在执行完Foo方法后会自动执行Bar函数,并且自动把Foo函数的返回值作为参数传入Bar函数
三、协程
协程,又称微线程,是一种用户态的轻量级线程。协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置,当程序中存在大量不需要CPU的操作时(IO),适用于协程。
协程有极高的执行效率,因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销。
不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
因为协程是一个线程执行,所以想要利用多核CPU,最简单的方法是多进程+协程,这样既充分利用多核,又充分发挥协程的高效率。
那符合什么条件就能称之为协程:1、必须在只有一个单线程里实现并发 2、修改共享数据不需加锁 3、用户程序里自己保存多个控制流的上下文栈 4、一个协程遇到IO操作自动切换到其它协程
python中对于协程有两个模块,greenlet和gevent。
Greenlet(greenlet的执行顺序需要我们手动控制)
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from greenlet import greenlet
def test1():
print (11)
gr2.switch() #手动切换
print (22)
gr2.switch() def test2():
print (33)
gr1.switch()
print (44) gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()
gevent(自动切换,由于切换是在IO操作时自动完成,所以gevent需要修改Python自带的一些标准库,这一过程在启动时通过monkey patch完成)
from gevent import monkey; monkey.patch_all()
import gevent
import time def foo():
print('')
time.sleep(3)
print('') def bar():
print('')
print('') gevent.joinall([
gevent.spawn(foo),
gevent.spawn(bar),
])
运行结果:(从结果可以看出,它们是并发执行的)
11
33
44
22
参考:
http://www.cnblogs.com/wupeiqi/articles/5040827.html
http://www.cnblogs.com/alex3714/articles/5230609.html
Python—进程、线程、协程的更多相关文章
- Python 进程线程协程 GIL 闭包 与高阶函数(五)
Python 进程线程协程 GIL 闭包 与高阶函数(五) 1 GIL线程全局锁 线程全局锁(Global Interpreter Lock),即Python为了保证线程安全而采取的独立线程运行的 ...
- python -- 进程线程协程专题
进程专栏 multiprocessing 高级模块 要让Python程序实现多进程(multiprocessing),我们先了解操作系统的相关知识. Unix/Linux操作系统提供了一个fork() ...
- python 进程 线程 协程
并发与并行:并行是指两个或者多个事件在同一时刻发生:而并发是指两个或多个事件在同一时间间隔内发生.在单核CPU下的多线程其实都只是并发,不是并行. 进程是系统资源分配的最小单位,进程的出现是为了更好的 ...
- python进程/线程/协程
一 背景知识 顾名思义,进程即正在执行的一个过程.进程是对正在运行程序的一个抽象. 进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一.操作系统的其他所 ...
- python 进程/线程/协程 测试
# Author: yeshengbao # -- coding: utf-8 -- # @Time : 2018/5/24 21:38 # 进程:如一个人拥有分身(分数数最好为cpu核心数)几乎同时 ...
- Python并发编程系列之常用概念剖析:并行 串行 并发 同步 异步 阻塞 非阻塞 进程 线程 协程
1 引言 并发.并行.串行.同步.异步.阻塞.非阻塞.进程.线程.协程是并发编程中的常见概念,相似却也有却不尽相同,令人头痛,这一篇博文中我们来区分一下这些概念. 2 并发与并行 在解释并发与并行之前 ...
- python自动化开发学习 进程, 线程, 协程
python自动化开发学习 进程, 线程, 协程 前言 在过去单核CPU也可以执行多任务,操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换任务2,任务2执行0.01秒,在切换到任务3,这 ...
- 进程&线程&协程
进程 一.基本概念 进程是系统资源分配的最小单位, 程序隔离的边界系统由一个个进程(程序)组成.一般情况下,包括文本区域(text region).数据区域(data region)和堆栈(stac ...
- 多道技术 进程 线程 协程 GIL锁 同步异步 高并发的解决方案 生产者消费者模型
本文基本内容 多道技术 进程 线程 协程 并发 多线程 多进程 线程池 进程池 GIL锁 互斥锁 网络IO 同步 异步等 实现高并发的几种方式 协程:单线程实现并发 一 多道技术 产生背景 所有程序串 ...
- python的进程/线程/协程
1.python的多线程 多线程就是在同一时刻执行多个不同的程序,然而python中的多线程并不能真正的实现并行,这是由于cpython解释器中的GIL(全局解释器锁)捣的鬼,这把锁保证了同一时刻只有 ...
随机推荐
- 常用JS表单验证方法
/*输入:str返回:如果全是空返回true,否则返回false*/function isNull(str) {if (str == "") return true;var reg ...
- thinkphp 的create()非法数据解决办法
是因为create()方法默认是使用post传值的,把from表单的传值方法改成post就行了,默认是get.
- HTML静态网页 标签、表格
HTML静态网页: 打开DREAMWEAVER,新建HTML,如下图: body的属性: bgcolor 页面背景色 background 背景壁纸.图片 text 文字颜色 topmargin ...
- VB鼠标指针
vbDefault 0 (缺省值)形状由对象决定. VbArrow 1 箭头. VbCrosshair 2 十字线(crosshair 指针). VbIbeam 3 I 型 VbIconPointer ...
- U3D--常用属性(不完整,待加)
1. AddComponentMenu 描述:这个属性可以在Component这个菜单栏下加自己定义的子菜单,这样可以快速添加常用的脚本.该属性要用在类上. using UnityEngine; us ...
- 示例-创建表格&使用表格对象
@charset "utf-8";/* CSS Document */table{ border:#249bdb 1px solid; width:500px; border-co ...
- a标签实用方法详解
a:link { color: black } /* 未访问时的状态 */ a:visited { color: blue } /* 已访问过的状态 */ a:hover { color: red } ...
- php如何遍历多维的stdClass Object 对象,php的转换成数组的函数只能转换外面一丛数组
php如何遍历多维的stdClass Object 对象,php的转换成数组的函数只能转换外面一丛数组 (2012-09-10 19:58:49) 标签: 杂谈 分类: 网页基础知识 php如何遍历多 ...
- Google Chrome浏览器中如何使用命令
Google Chrome浏览器中如何使用命令 | 浏览:2974 | 更新:2014-02-23 23:12 | 标签:chrome 1 2 3 分步阅读 Google Chrome浏览器有很多的特 ...
- ModelAndView学习整理
ModelAndView mav = new ModelAndView("/media/play-video");是什么意思 1.这是SpringMVC里面的问题啊!2.这叫返回一 ...