进程、线程、协程
一、进程与线程
进程:程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。
线程:线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
进程与线程区别:1、线程是指令集,共享内存空间,启动速度快;进程是资源的集合,至少包含一个线程,内存独立;
2、同一进程的线程之间可以直接交流,两个进程需要通过中间代理实现;
3、创建新的线程简单,创建新的进程需要对其父进程进行克隆;一个线程可以控制和操作同一进程的其他线程,但进程只能操作子进程;
4、运行速度一样,无可比性。
python threading模块:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import threading,time 2 def run(n): #定义每个线程执行的函数 3 print("task",n) 4 time.sleep(2) 5 t1 = threading.Thread(target=run,args=("1",)) #生成线程实例 6 t2 = threading.Thread(target=run,args=("2",)) 7 t1.start() #启动 8 t2.start() 9 print(t1.getName()) #获取线程名 10 print(t2.getName())
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import threading,time 2 class MyThread(threading.Thread): 3 def __init__(self,n): 4 #threading.Thread.__init__(self) #两种继承方式 5 super(MyThread,self).__init__() 6 self.n = n 7 def run(self): 8 print("task",self.n) 9 time.sleep(2) 10 if __name__ == "__main__": #主线程的意思 11 t1 = MyThread(1) 12 t2 = MyThread(2) 13 t1.start() 14 t2.start()
join&daemon:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import threading,time 2 def run(n): 3 print("task",n) 4 time.sleep(2) 5 print("task done.") 6 def main(): 7 for i in range(5): 8 t = threading.Thread(target=run, args=(i,)) 9 t.start() 10 t.join(1) 11 print('starting thread', t.getName()) 12 m = threading.Thread(target=main,args=()) 13 m.setDaemon(True) #将main线程设置为Daemon线程,它做为程序主线程的守护线程,当主线程退出时,m线程也会退出,由m启动的其它子线程会同时退出,不管是否执行完任务 14 m.start() 15 m.join(2) 16 print("---main thread done---")
线程锁(互斥锁Mutex)
Semaphore(信号量)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import threading, time 2 def run(n): 3 semaphore.acquire() 4 time.sleep(1) 5 print("run the thread: %s" % n) 6 semaphore.release() 7 if __name__ == '__main__': 8 num = 0 9 semaphore = threading.BoundedSemaphore(3) # 最多允许3个线程同时运行 10 for i in range(13): 11 t = threading.Thread(target=run, args=(i,)) 12 t.start() 13 14 while threading.active_count() != 1: 15 pass # print threading.active_count() 16 else: 17 print('----all threads done---')
event:通过Event来实现两个或多个线程间的交互
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import threading,time 2 event = threading.Event() 3 def lighter(): 4 count = 0 5 event.set() 6 while True: 7 if count > 5 and count < 10: 8 event.clear() 9 print("\033[41;1m red light is on..\033[0m") 10 elif count > 10: 11 event.set() 12 count = 0 13 else: 14 print("\033[42;1m green light is on..\033[0m") 15 time.sleep(1) 16 count += 1 17 def car(name): 18 while True: 19 if event.is_set(): 20 print("[%s] running.." %name) 21 time.sleep(1) 22 else: 23 print("[%s] sees red light,waiting.." %name) 24 event.wait() 25 print("\033[34;1m [%s] green light is on,start going..\033[0m" %name) 26 light = threading.Thread(target=lighter) 27 light.start() 28 car1 = threading.Thread(target=car,args=("car",)) 29 car1.start()
线程queue,线程之间,生产者消费者模型:解耦,使程序直接实现松耦合,提高效率
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 queue.Queue(maxsize=0) #先入先出 2 queue.LifoQueue(maxsize=0) #last in first out 3 queue.PriorityQueue(maxsize=0) #存储数据时可设置优先级的队列 4 Queue.qsize() 5 Queue.empty() #return True if empty 6 Queue.full() # return True if full 7 Queue.put(item, block=True, timeout=None) 8 Queue.put_nowait(item) #Equivalent to put(item, False) 9 Queue.get(block=True, timeout=None) 10 Queue.get_nowait() #Equivalent to get(False). 11 Queue.task_done() 12 Queue.join() #block直到queue被消费完毕
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import threading,queue,time 2 def producer(name): 3 count = 1 4 while True: 5 q.put("包子 %s" % count) 6 print("\033[41;1m生产了包子\033[0m",count) 7 count += 1 8 time.sleep(0.3) 9 def consumer(name): 10 #while q.qsize() > 0: 11 while True: 12 print("%s 取到" % name, q.get()) 13 time.sleep(1) 14 #q.task_done() # 告知这个任务执行完了 15 q = queue.Queue(maxsize=10) 16 p = threading.Thread(target=producer,args=("a",)) 17 c = threading.Thread(target=consumer,args=("b",)) 18 c2 = threading.Thread(target=consumer,args=("c",)) 19 p.start() 20 c.start() 21 c2.start()
多进程:每个子进程都是由父进程启动
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import multiprocessing 2 import time 3 def f(name): 4 time.sleep(2) 5 print('hello', name) 6 if __name__ == '__main__': 7 for i in range(10): 8 p= multiprocessing.Process(target=f, args=('bob%s'%i,)) 9 p.start() 10 #p.join()
进程Queue:进程间内存独立,实现进程间数据交换相当于复制一份数据
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 from multiprocessing import Process, Queue 2 def f(q): 3 q.put([1, None, 'hello world']) 4 if __name__ == '__main__': 5 q = Queue() 6 p = Process(target=f, args=(q,)) 7 p.start() 8 print(q.get()) # prints "[42, None, 'hello']" 9 p.join()
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 from multiprocessing import Process, Pipe 2 def f(conn): 3 conn.send([1, None, 'hello a']) 4 print("from parent:",conn.recv()) 5 conn.close() 6 7 if __name__ == '__main__': 8 parent_conn, child_conn = Pipe()#生成实例 9 p = Process(target=f, args=(child_conn,)) 10 p.start() 11 parent_conn.send("hello ab") 12 print(parent_conn.recv()) # prints "[1, None, 'hello a']" 13 p.join()
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 from multiprocessing import Process, Manager 2 import os 3 def f(d, l): 4 d[os.getpid()] = os.getpid() 5 l.append(os.getpid()) 6 print(l) 7 8 if __name__ == '__main__': 9 with Manager() as manager: 10 d = manager.dict() 11 l = manager.list(range(5)) 12 p_list = [] 13 for i in range(10): 14 p = Process(target=f, args=(d, l)) 15 p.start() 16 p_list.append(p) 17 for res in p_list: 18 res.join() 19 20 print(d) 21 print(l)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 from multiprocessing import Process, Lock 2 def f(l, i): 3 l.acquire() 4 print('hello world', i) 5 l.release() 6 7 if __name__ == '__main__': 8 lock = Lock() 9 for num in range(10): 10 Process(target=f, args=(lock, num)).start()
进程池
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 from multiprocessing import Process, Pool,freeze_support #windows加freeze_support 2 import time,os 3 def Foo(i): 4 time.sleep(2) 5 print("in process:",os.getpid()) 6 return i + 100 7 def Bar(arg): 8 print('-->exec done:', arg) 9 if __name__ == "__main__": 10 pool = Pool(5) #同时放入5个进程 11 for i in range(10): 12 pool.apply_async(func=Foo, args=(i,), callback=Bar)#异步,callback为回调函数,由主进程执行 13 # pool.apply(func=Foo, args=(i,)) #同步 14 print('end') 15 pool.close() 16 pool.join() # 进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。
二、协程
协程,又称微线程,是一种用户态的轻量级线程。
协程拥有自己的上下文和栈,协程调度切换时,将寄存器上下文和栈保存到其他地方,在切换回来时,恢复先前的寄存器上下文和栈,因此协程能保留上一次调用时的状态,每次重入时进入上次离开时逻辑流的位置。
协程的好处:
- 无需上下文切换的开销;
- 无需原子操作和锁定的开销;
- 方便切换控制流,简化编程模型;
- 高并发,搞扩展,低成本。
缺点:
- 无法运用多核资源:本质是单线程,需和进程配合才能运用到多核;
- 进行阻塞(blocking)时(如IO操作)会阻塞整个程序。
定义:
- 必须在只有一个单线程内实现并发;
- 修改共享数据无需加锁;
- 用户程序里自己保存多个控制流的上下文栈;
- 一个协程遇到IO操作自动切换到其他协程。
Greenlet:手动switch切换
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 from greenlet import greenlet 2 def run1(): 3 print("1") #first 4 g2.switch() #2 5 print("2") #5 6 g2.switch() #6 7 def run2(): 8 print("3") #3 9 g1.switch() #4 10 print("4") #7 11 12 g1 = greenlet(run1) 13 g2 = greenlet(run2) 14 g1.switch() 15 g2.switch()
Gevent:遇到IO自动切换
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import gevent 2 def run1(): 3 print("1") 4 gevent.sleep(2) 5 print("2") 6 def run2(): 7 print("3") 8 gevent.sleep(1) 9 print("4") 10 11 gevent.joinall([ 12 gevent.spawn(run1), 13 gevent.spawn(run2) 14 ]) 15 #输出: 16 #1 17 #3 18 #4 19 #2
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 from urllib import request 2 import gevent,time 3 from gevent import monkey 4 monkey.patch_all()#把当前程序的所有IO操作单独做上标记 5 def f(url): 6 print("GET:%s" %url) 7 resp = request.urlopen(url) 8 data = resp.read() 9 print("%d bytes received from %s." %(len(data),url)) 10 urls = [ 11 "https://www.python.org/", 12 "https://www.yahoo.com/", 13 "https://www.github.com/" 14 ] 15 time_start = time.time() 16 for url in urls: 17 f(url) 18 print("同步cost:",time.time()-time_start) 19 async_time = time.time() 20 gevent.joinall([ 21 gevent.spawn(f,"https://www.python.org/"), 22 gevent.spawn(f,"https://www.yahoo.com/"), 23 gevent.spawn(f,"https://www.github.com/") 24 ]) 25 print("异步cost:",time.time()-async_time) 26 27 #输出:时间与网速有关,但异步要比同步少 28 GET:https://www.python.org/ 29 48845 bytes received from https://www.python.org/. 30 GET:https://www.yahoo.com/ 31 501665 bytes received from https://www.yahoo.com/. 32 GET:https://www.github.com/ 33 65079 bytes received from https://www.github.com/. 34 同步cost: 6.50391411781311 35 GET:https://www.python.org/ 36 GET:https://www.yahoo.com/ 37 GET:https://www.github.com/ 38 65077 bytes received from https://www.github.com/. 39 48845 bytes received from https://www.python.org/. 40 502067 bytes received from https://www.yahoo.com/. 41 异步cost: 3.0211377143859863
通过Gevet实现socket并发
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 from gevent import socket,monkey 2 import socket,time,gevent 3 monkey.patch_all() 4 def server(port): 5 s = socket.socket() 6 s.bind(("localhost",port)) 7 s.listen() 8 while True: 9 conn,addr = s.accept() 10 gevent.spawn(handle_request,conn) 11 def handle_request(conn): 12 try: 13 while True: 14 data = conn.recv(1024) 15 print("recv:",data) 16 conn.send(data) 17 if not data: 18 conn.shutdown() 19 except Exception as e: 20 print(e) 21 finally: 22 conn.close() 23 if __name__ == "__main__": 24 server(8001)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import socket 2 HOST = "localhost" 3 PORT = 8001 4 s = socket.socket() 5 s.connect((HOST,PORT)) 6 while True: 7 msg = bytes(input(">>:"),encoding="utf-8") 8 s.sendall(msg) 9 data = s.recv(1024) 10 print("Received:",data) 11 s.close()