Python 10:线程进程协程异步io

Posted 水无

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python 10:线程进程协程异步io相关的知识,希望对你有一定的参考价值。

Python 10:线程、进程、协程、异步io


1、概念

2、线程

3、进程

4、协程

5、事件件驱动模型

6、异步io

7、作业


 一、概念

1、进程:

程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。

(程序以一个整体的形式暴露给操作系统。里面包含对各种资源的调用,内存的管理,网络接口的调用等,即对各种资源管理的集合)

程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。

启动一个进程会自动启动一个线程,进程里的第一个线程就是主线程,主线程可以创建子线程

2、线程:

线程是操作系统能够进行运算调度的最小单位,是一串指令的集合。

它被包含在进程之中,是进程中的实际运作单位。

一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务

进程要操作cpu,至少创建一个线程

所有在同一个进程里的线程共享同一块内存空间

3、区别:

线程共享内存空间,进程内存是独立的

多个线程可以直接访问同一个进程的数据,多个子进程之间数据是相互独立的

同一个进程的线程之间可以直接交流,两个进程想通信必须通过一个中间代理来实现

创建新的线程很简单,创建新的进程需要对其父进程进行一次克隆

一个线程可以控制和操作同一进程里的其他线程,但是进程只能操作子进程

对于主线程的修改有可能会影响到其他线程的运行,但是对一个父进程的修改不会影响其他子进程 

二、线程

1、调用

 1 import threading
 2 import time
 3 def run(n):
 4     print("task",n)
 5     time.sleep(2)
 6 t1 = threading.Thread(target=run,args=("t1",))
 7 t2 = threading.Thread(target=run,args=("t2",))
 8 t1.start()   #并行
 9 t2.start()
10 # run("t1")  #未采用多线程:串行
11 # run("t2")
多线程直接调用
 1 import threading
 2 class MyThread(threading.Thread):
 3     def __init__(self,n):
 4         super(MyThread,self).__init__()
 5         self.n = n
 6     def run(self):
 7         print("running task",self.n)
 8 t1 = MyThread("t1")
 9 t2 = MyThread("t2")
10 t1.start()
11 t2.start()
多线程继承调用

2、 Join 和 Daemon 

 1 import threading
 2 import time
 3 def run(n):
 4     print("task",n,threading.current_thread())   #<Thread(Thread-1, started 1516)>
 5     time.sleep(2)
 6 start_time = time.time()
 7 t_objs = []
 8 for i in range(10):
 9     t = threading.Thread(target=run,args=("t-%s"%i,))
10     t.start()
11     t_objs.append(t)
12 for t in t_objs:
13     t.join()     #在主线程里等待子线程结果
14 print("who:",threading.current_thread(),threading.active_count())  #打印当前线程 <_MainThread(MainThread, started 6568)> 和当前活动线程数
15 print("cost:",time.time() - start_time) #为加join主线程与子线程并行,不会等子线程运行结束
join
 1 import threading
 2 import time
 3 def run(n):
 4     print("task",n,threading.current_thread())   #<Thread(Thread-1, started 1516)>
 5     time.sleep(2)
 6 start_time = time.time()
 7 t_objs = []
 8 for i in range(10):
 9     t = threading.Thread(target=run,args=("t-%s"%i,))
10     t.setDaemon(True)  #把当前线程设置为守护线程(主进程结束时不会等待守护线程执行完毕)
11     t.start()  #一定要在执行之前设置
12     t_objs.append(t)
13 # for t in t_objs:
14 #     t.join()     #在主线程里等待子线程结果
15 print("who:",threading.current_thread(),threading.active_count())  #打印当前线程 <_MainThread(MainThread, started 6568)> 和当前活动线程数
16 print("cost:",time.time() - start_time) #为加join主线程与子线程并行,不会等子线程运行结束
17 
18 #程序正常退出会等待所有进程执行完毕(守护进程除外)
deamon

3、GIL

全局解释器锁 :无论你启多少个线程,你有多少个cpu, Python在执行的时候在同一时刻只允许一个线程运行。

4、线程锁(互斥锁Mutex)

一个进程下可以启动多个线程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问同一份数据,此时,如果2个线程同时要修改同一份数据,有可能线程1还没修改完成但是线程2已经修改完成,为了避免线程自己在还没改完的时候别人也来修改此数据,可以给这个数据加一把锁, 这样其它线程想修改此数据时就必须等待你修改完毕并把锁释放掉后才能再访问此数据。 

 1 import threading
 2 def addNum():
 3     lock.accquire()                 #加锁:只允许一个线程修改数据,在这里把线程变成了串行
 4     global num #在每个线程中都获取这个全局变量
 5     print(\'--get num:\',num )
 6     num  +=1 #对此公共变量进行+1操作
 7     lock.release()                 #释放锁
 8 lock = threading.Lock()         #申请锁
 9 num = 0  #设定一个全局变量
10 thread_list = []
11 for i in range(10):
12     t = threading.Thread(target=addNum)
13     t.start()
14     thread_list.append(t)
15 for t in thread_list: #等待所有线程执行完毕
16     t.join()
17 print(\'final num:\', num )
互斥锁

5、递归锁

就是在一个大锁中还要再包含子锁,即有多个锁同时存在,当释放锁的时候系统自己混乱对不上号

递归锁的存在让程序能分辨出每个锁所对应的钥匙

 1 import threading,time
 2 
 3 def run1():
 4     print("grab the first part data")
 5     lock.acquire()
 6     global num
 7     num +=1
 8     lock.release()
 9     return num
10 def run2():
11     print("grab the second part data")
12     lock.acquire()
13     global  num2
14     num2+=1
15     lock.release()
16     return num2
17 def run3():
18     lock.acquire()
19     res = run1()
20     print(\'--------between run1 and run2-----\')
21     res2 = run2()
22     lock.release()
23     print(res,res2)
24 
25 
26 if __name__ == \'__main__\':
27 
28     num,num2 = 0,0
29     lock = threading.RLock()   #递归锁()
30     for i in range(10):
31         t = threading.Thread(target=run3)
32         t.start()
33 
34 while threading.active_count() != 1:
35     print(threading.active_count())
36 else:
37     print(\'----all threads done---\')
38     print(num,num2)
递归锁

6、Semaphore(信号量)

互斥锁 :同时只允许一个线程更改数据,而Semaphore是同时只允许一定数量的线程更改数据

 1 import threading,time
 2 
 3 def run(n):
 4     semaphore.acquire()
 5     time.sleep(1)
 6     print("run the thread: %s\\n" %n)
 7     semaphore.release()
 8 
 9 semaphore  = threading.BoundedSemaphore(5) #最多允许5个线程同时运行
10 for i in range(20):
11     t = threading.Thread(target=run,args=(i,))
12     t.start()
13 while threading.active_count() != 1:
14     pass
15 else:
16     print(\'----all threads done---\')
信号量

7、event(线程交互)

通过Event来实现两个或多个线程间的交互

 1 import time
 2 import threading
 3 
 4 event = threading.Event()
 5 def light():
 6     count = 0
 7     event.set()
 8     while True:
 9         if count > 5 and count <= 10: #变成红灯
10             event.clear()  #清空标志位
11             print("\\033[41;1mred light is on ...\\033[0m")
12         elif count > 10:
13             event.set()  #变绿灯
14             count = 0
15         else:
16             print("\\033[42;1mgreen light is on ...\\033[0m")
17         time.sleep(1)
18         count += 1
19 
20 def car(name):
21     while True:
22         if event.is_set():  #判断是否设置了标志位
23             print("[%s] running ..."%name)
24             time.sleep(1)
25         else:
26             print("[%s] see red light ,waiting..."%name)
27             event.wait()
28             print("[%s] green light is on start going ..."%name)
29 
30 light = threading.Thread(target=light,)
31 light.start()
32 car1 = threading.Thread(target=car,args=("car_1",))
33 car1.start()
红绿灯模型

8、queue队列 

import queue
q = queue.Queue()   #先入先出队列
# q = queue.Queue(maxsize=2) #队列接受最大容量
q.put("a")   #往队列里放入数据
q.put("b")
q.put("c")
print(q.qsize() )    #队列大小
q.get()    #从队列里取数据
q.get()

qq = queue.LifoQueue()  #后进先出对列
qq.put("a")   #往队列里放入数据
qq.put("b")
qq.put("c")
print(qq.get())  #c

qqq = queue.PriorityQueue()  #存储数据时可设置优先级的队列(vip)
qqq.put((9,"a"))  #放入元组,队列将按照元组第一个值进行排序
qqq.put((5,"b"))
qqq.put((7,"c"))
qqq.put((3,"d"))
print(qqq.get())   #(3, \'d\')
print(qqq.get())   #(5, \'b\')
queue

9、生产者消费者模型

 1 import threading,time
 2 import queue
 3 def producer():
 4     count = 1
 5     while True:
 6         q.put("骨头 %s" % count )
 7         print("生产了骨头%s"%count)
 8         count += 1
 9         time.sleep(2)
10 def consumer(n):
11     # while q.qsize() >0:
12     while True:
13         print("%s 取到" %n, q.get())
14         time.sleep(1)
15 
16 q = queue.Queue(maxsize=10)
17 p = threading.Thread(target=producer,)
18 p.start()
19 c1 = threading.Thread(target=consumer,args=("aa",))
20 c1.start()
21 c2 = threading.Thread(target=consumer,args=("bb",))
22 c2.start()
生产者消费者模型

 三、进程

在python中由于GIL的存在,无论你启多少个线程,你有多少个cpu, Python在执行的时候在同一时刻只允许一个线程运行

所以python的多线程不适合cpu密集操作型的任务,适合io密集型的任务(io操作不占用cpu、计算占用cpu)

注意:如果在windows中启动多进程必须采用:if __name__ =="__main__"(区分是自己主动执行还是被其他程序调用)

1、调用 

 1 import multiprocessing,threading
 2 import time
 3 
 4 def run(name):
 5     time.sleep(1)
 6     print("hello",name)
 7     t = threading.Thread(target=thread_run())
 8     t.start()
 9 
10 def thread_run():
11     print(threading.get_ident())
12 
13 if __name__ == \'__main__\':
14     for i in range(10):
15         p = multiprocessing.Process(target=run,args=("aa",))
16         p.start()
多进程

2、父进程、子进程:每一个进程都是由父进程启动的

 1 from multiprocessing import Process
 2 import os
 3 def info(title):
 4     print(title)
 5     print(\'module name:\', __name__)
 6     print(\'parent process:\', os.getppid())
 7     print(\'process id:\', os.getpid())
 8     print("\\n\\n")
 9 def f(name):
10     info(\'\\033[31;1mfunction f\\033[0m\')
11     print(\'hello\', name)
12 if __name__ == \'__main__\':
13     info(\'\\033[32;1mmain process line\\033[0m\')
14     p = Process(target=f, args=(\'bob\',))
15     p.start()
16     p.join()
父进程、子进程

3、进程间通信:

3.1进程间数据的传递

1 from multiprocessing import Process, Queue
2 def f(q):
3     q.put([42, None, \'hello\'])
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()
进程queue
 1 from multiprocessing import Process, Pipe
 2 
 3 def f(conn):
 4     conn.send([42, None, \'hello from child\'])
 5     print(conn.recv())
 6     conn.close()
 7 
 8 if __name__ == \'__main__\':
 9     parent_conn, child_conn = Pipe()   #管道一生成就会产生两个返回对象
10     p = Process(target=f, args=(child_conn,))
11     p.start()
12     print(parent_conn.recv())   # prints "[42, None, \'hello\']"
13     parent_conn.send("hello from parent")
14     p.join()
pipes

3.2进程间数据的共享

 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         print(d)
20         print(l)
manager

4、进程锁:因为屏幕是共享的,多个进程可能同时使用屏幕输出,防止输出错乱而加锁

 1 from multiprocessing import Process, Lock
 2  
 3 def f(l, i):
 4     l.acquire()
 5     try:
 6         print(\'hello world\', i)
 7     finally:
 8         l.release() 
 9 if __name__ == \'__main__\':
10     lock = Lock() 
以上是关于Python 10:线程进程协程异步io的主要内容,如果未能解决你的问题,请参考以下文章

python之异步IO

Python基础15 - 协程异步IO

# 进程/线程/协程 # IO:同步/异步/阻塞/非阻塞 # greenlet gevent # 事件驱动与异步IO # SelectPollEpoll异步IO 以及selectors模块 # (示

python—day32 异步 + 回调 Eventgevent 协程单线程下实现遇到IO切换

Python异步IO

python---爬虫相关性能(各个异步模块的使用,和自定义异步IO模块)