python 线程、进程和协程
来源:互联网 发布:英雄岛全套源码下载 编辑:程序博客网 时间:2024/04/29 19:49
一:线程基础:
线程与进程的不同之处在于,它们共享状态、内存和资源。对于线程来说,这个简单的区别既是它的优势,又是它的缺点。一方面,线程是轻量级的,并且相互之间易于通信,但另一方面,它们也带来了包括死锁、争用条件和高复杂性在内的各种问题。幸运的是,由于 GIL 和队列模块,与采用其他的语言相比,采用 Python 语言在线程实现的复杂性上要低得多。
1:线程分为5种状态
Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元。
#!/usr/bin/env python# -*- coding:utf-8 -*-import threadingimport time def show(arg): time.sleep(1) print('thread'+str(arg)) for i in range(10): t = threading.Thread(target=show, args=(i,)) t.start() print('main thread stop')
上述代码创建了10个“前台”线程,然后控制器就交给了CPU,CPU根据指定算法进行调度,分片执行指令。
更多方法:
- start 线程准备就绪,等待CPU调度
- setName 为线程设置名称
- getName 获取线程名称
- setDaemon 设置为后台线程或前台线程(默认)
如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止
如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止 - join 逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义
- run 线程被cpu调度后自动执行线程对象的run方法
2:线程锁
多线程的优势在于可以同时运行多个任务(至少感觉起来是这样)。但是当线程需要共享数据时,可能存在数据不同步的问题。考虑这样一种情况:一个列表里所有元素都是0,线程"set"从后向前把所有元素改成1,而线程"print"负责从前往后读取列表并打印。那么,可能线程"set"开始改的时候,线程"print"便来打印列表了,输出就成了一半0一半1,这就是数据的不同步。为了避免这种情况,引入了锁的概念。
锁有两种状态——锁定和未锁定。每当一个线程比如"set"要访问共享数据时,必须先获得锁定;如果已经有别的线程比如"print"获得锁定了,那么就让线程"set"暂停,也就是同步阻塞;等到线程"print"访问完毕,释放锁以后,再让线程"set"继续。经过这样的处理,打印列表时要么全部输出0,要么全部输出1,不会再出现一半0一半1的尴尬场面。
由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,CPU接着执行其他线程。所以,可能出现如下问题:
1.1:未使用锁
#!/usr/bin/env python# -*- coding:utf-8 -*-import threadingimport timegl_num = 0def show(arg): global gl_num time.sleep(1) gl_num +=1 print(gl_num)for i in range(10): t = threading.Thread(target=show, args=(i,)) t.start()print('main thread stop')使用锁
#!/usr/bin/env python#coding:utf-8import threadingimport timegl_num = 0lock = threading.RLock() def Func(): lock.acquire() global gl_num gl_num +=1 time.sleep(1) print(gl_num) lock.release()for i in range(10): t = threading.Thread(target=Func) t.start()二:进程
from multiprocessing import Processimport threadingimport time def foo(i): print('say hi',i) for i in range(10): p = Process(target=foo,args=(i,)) p.start()由于进程之间的数据需要各自持有一份,所以创建进程需要的非常大的开销。
2.1:进程数据共享
进程各自持有一份数据,默认无法共享数据
默认无法共享的例子
#!/usr/bin/env python#coding:utf-8 from multiprocessing import Processfrom multiprocessing import Manager import time li = [] def foo(i): li.append(i) print('say hi',li) for i in range(10): p = Process(target=foo,args=(i,)) p.start() print('ending',li)进程共享
#方法一,Arrayfrom multiprocessing import Process,Arraytemp = Array('i', [11,22,33,44]) def Foo(i): temp[i] = 100+i for item in temp: print(i,'----->',item) for i in range(2): p = Process(target=Foo,args=(i,)) p.start() #方法二:manage.dict()共享数据from multiprocessing import Process,Manager manage = Manager()dic = manage.dict() def Foo(i): dic[i] = 100+i print(dic.values()) for i in range(2): p = Process(target=Foo,args=(i,)) p.start() p.join()当创建进程时(非使用时),共享数据会被拿到子进程中,当进程中执行完毕后,再赋值给原值。
进程锁例
#!/usr/bin/env python# -*- coding:utf-8 -*-from multiprocessing import Process, Array, RLockdef Foo(lock,temp,i): """ 将第0个数加100 """ lock.acquire() temp[0] = 100+i for item in temp: print(i,'----->',item) lock.release()lock = RLock()temp = Array('i', [11, 22, 33, 44])for i in range(20): p = Process(target=Foo,args=(lock,temp,i,)) p.start()2.2:进程池
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。
进程池中有两个方法:
- apply
- apply_async
#!/usr/bin/env python# -*- coding:utf-8 -*-from multiprocessing import Process,Poolimport time def Foo(i): time.sleep(2) return i+100 def Bar(arg): print(arg) pool = Pool(5)#print pool.apply(Foo,(1,))#print pool.apply_async(func =Foo, args=(1,)).get() for i in range(10): pool.apply_async(func=Foo, args=(i,),callback=Bar) print('end')pool.close()pool.join()#进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。
三:协程
线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员。
协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。
协程的适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程;
greenlet
#!/usr/bin/env python# -*- coding:utf-8 -*- from greenlet import greenlet def test1(): print(12) gr2.switch() print(34) gr2.switch() def test2(): print(56) gr1.switch() print(78) gr1 = greenlet(test1)gr2 = greenlet(test2)gr1.switch()3.1:gevent
import gevent def foo(): print('Running in foo') gevent.sleep(0) print('Explicit context switch to foo again') def bar(): print('Explicit context to bar') gevent.sleep(0) print('Implicit context switch back to bar') gevent.joinall([ gevent.spawn(foo), gevent.spawn(bar),])
遇到IO操作自动切换:
from gevent import monkey; monkey.patch_all()import geventimport urllib2def f(url): print('GET: %s' % url) resp = urllib2.urlopen(url) data = resp.read() print('%d bytes received from %s.' % (len(data), url))gevent.joinall([ gevent.spawn(f, 'https://www.python.org/'), gevent.spawn(f, 'https://www.yahoo.com/'), gevent.spawn(f, 'https://github.com/'),])
- python 线程、进程和协程
- Python线程、进程和协程详解
- 线程、进程和协程
- 线程、进程和协程
- 线程、进程和协程
- 进程、线程和协程
- Python之路【第七篇】:线程、进程和协程
- Python之路【第七篇】:线程、进程和协程
- [转]进程、线程和协程
- 进程、线程和协程的理解
- 进程、线程和协程的理解
- 进程、线程和协程的理解
- 进程、线程和协程的理解
- 进程、线程和协程的理解
- 进程、线程和协程的理解
- 进程、线程和协程的理解
- 进程、线程和协程的理解
- 进程、线程和协程的区别是什么
- webview总结
- C#中DropDownList显示默认值
- 2016.03.21 java web摘抄
- 第八章 网络的时代—网络开发(4)
- springMVC—三种控制器controller
- python 线程、进程和协程
- UITableView beginUpdates和endUpdates-实现UITableView的动画块
- Java面试题全集(上)
- iOS-OC-NSString和NSMutableString用法大全
- PHP进阶
- Console 相关操作
- 网站高并发(一)
- Inventor API: 简单聊聊ReplaceReference , FileSaveAs, OnFileResolution
- iOS-OC-NSDictionary和NSMutableDictionary用法大全详细说明