并发编程 - 多线程 - 总结

进程,线程:
进程只是用来把资源集中到一起的(进程只是个资源单位,或者说资源集合),线程才是cpu上的执行单位。
区别:
1.同一个进程内的多个线程共享该进程内的地址资源
2.创建线程的开销要远小于创建进程的开销(创建一个进程,就是创建一个车间,涉及到申请空间,而且在该空间内建至少一条流水线,但创建线程,就只是在一个车间内造一条流水线,无需申请空间,所以创建开销小
1.创建线程的两种方式:
 1 import time
 2 from threading import Thread
 3 
 4 def sayhi(name):
 5     time.sleep(2)
 6     print('%s is say hello'%name)
 7 
 8 if __name__ == "__main__":
 9     t = Thread(target=sayhi,kwargs={'name':'alice'})
10     # t = Thread(target=sayhi,args=('alice',))
11     t.start()
12 
13     print('主线程')
方式一
 1 from threading import Thread
 2 import time
 3 
 4 class Sayhi(Thread):
 5     def __init__(self,name):
 6         super().__init__()
 7         self.name = name
 8 
 9     def run(self):
10         time.sleep(2)
11         print('%s is say hello'%self.name)
12 
13 if __name__ == "__main__":
14     t = Sayhi('alice')
15     t.start()
16 
17     print('主线程')
方式二
2.多进程多线程的区别: 
1.开进程的开销远大于开线程
2.进程之间地址空间是隔离的,
同一进程内开启的多个线程是共享该进程的地址空间的
3.开多个进程,每个进程都有不同的pid
在主进程下开启多个线程,每个线程的pid都和主进程的pid一样
 1 #1.
 2 from multiprocessing import Process
 3 from threading import Thread
 4 import time
 5 
 6 def work(name):
 7     print('%s is working'%name)
 8     time.sleep(2)
 9     print('%s is done'%name)
10 
11 if __name__ == "__main__":
12     # p = Process(target=work,args=('alice',))
13     # p.start()
14 
15     t = Thread(target=work,args=('alice',))
16     t.start()
17 
18     print('主进程/主线程')
19 """
20 主进程/主线程
21 alice is working
22 alice is done
23 """
24 """
25 alice is working
26 主进程/主线程
27 alice is done
28 """
29 #2.
30 from multiprocessing import Process
31 from threading import Thread
32 
33 n=100
34 def task():
35     global n
36     n=0
37 
38 if __name__ == "__main__":
39     # p = Process(target=task)
40     # p.start()
41     # p.join()   # 彼此隔离的 n: 100
42     t = Thread(target=task,)
43     t.start()
44     t.join()   # 共享地址空间 n: 0
45     print('n:',n)
46 """
47 n: 100
48 """
49 """
50 n: 0
51 """
52 # 3.
53 from multiprocessing import Process,current_process
54 from threading import Thread
55 import os
56 
57 def task():
58     print(current_process().pid)
59     print('hello,pid:',os.getpid(),"ppid:",os.getppid())
60 
61 if __name__ == "__main__":
62     p1 = Process(target=task)
63     p2 = Process(target=task)
64     p1.start()
65     p2.start()
66     # t1 = Thread(target=task)
67     # t2 = Thread(target=task)
68     # t1.start()
69     # t2.start()
70 
71     print(current_process().pid)
72     print('主进程/主线程 pid:',os.getpid(),'ppid:',os.getppid())
73 """
74 主进程/主线程 pid: 6908 ppid: 3888
75 hello,pid: 21096 ppid: 6908
76 hello,pid: 20228 ppid: 6908
77 """
78 """
79 hello,pid: 24068 ppid: 3888
80 hello,pid: 24068 ppid: 3888
81 主进程/主线程 pid: 24068 ppid: 3888
82 """
多进程多线程的区别
3.Thread对象的其他属性或方法:
Thread实例化对象的方法:
1.t.isAlive() / t.is_alive() # 返回线程是否活动中
2.t.getName() / t.name # 返回线程名
3.t.setName() # 设置线程名
threading模块提供的一些方法:
threading.currentThread() # 返回当前的线程变量
threading.enumerate() # 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
threading.activeCount() # 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
主线程等待子线程结束!!
 1 from threading import Thread,currentThread,current_thread,active_count,enumerate,activeCount
 2 import time
 3 
 4 def task():
 5     print('%s is running'%currentThread().getName())
 6     time.sleep(2)
 7     print('%s is done'%current_thread().getName())
 8 
 9 if __name__ == "__main__":
10     t = Thread(target=task,)
11     t.start()
12     # t.join()
13     print(t.getName())
14     t.setName('儿子')
15     print(t.name)
16     currentThread().setName('主线程')
17     print(current_thread().name)
18     print(t.is_alive())
19     # t.join()
20     print(active_count())
21     print(enumerate())
22     print(t.isAlive())
23     print(t.name)
24     print(t.getName())
25     # currentThread().
26     print(len(enumerate()))
27     print(activeCount())
28     print(active_count())
29     # t.join()
30     print(t.isAlive())
31     time.sleep(3)
32     print(t.isAlive())
Thread对象的其他属性或方法
4.守护线程:
守护进程(守护线程)会等待主进程(主线程)运行完毕后被销毁
运行完毕并非终止运行:
1.对主进程来说:运行完毕指的是主进程代码运行完毕
2.对主线程来说:运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕
详细解释:
1.主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束。
2.主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束。

t.daemon = True
t.setDaemon(True)
 1 from threading import Thread
 2 import time
 3 
 4 def sayhi(name):
 5     print('start-------')
 6     time.sleep(2)
 7     print('%s say hello'%name)
 8 
 9 if __name__ == "__main__":
10     t = Thread(target=sayhi,args=('alice',))
11     # t.daemon = True
12     t.setDaemon(True)
13     t.start()
14 
15     print('主线程')
16     print(t.is_alive())
17 """
18 start-------
19 主线程
20 True
21 """
22 
23 # 思考下述代码的执行结果有可能是哪些情况?为什么?
24 from threading import Thread
25 import time
26 
27 def foo():
28     print(123)
29     time.sleep(1)
30     print("end123")
31 
32 def bar():
33     print(456)
34     time.sleep(3)
35     print("end456")
36 
37 if __name__ == '__main__':
38     t1=Thread(target=foo)
39     t2=Thread(target=bar)
40 
41     t1.daemon=True
42     t1.start()
43     t2.start()
44     print("main-------")
45 """   # 主线程运行完毕 指的是 所有的非守护线程统统运行完毕
46 123
47 456
48 main-------
49 end123
50 end456
51 """
守护线程
5.互斥锁:  
在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势。
GIL并不是Python的特性,Python完全可以不依赖于GIL。像其中的JPython就没有GIL。
互斥锁:
原理:将并行变成串行
精髓:局部串行,只针对共享数据修改
保护不同的数据就应该用不用的锁
 1 from threading import Thread,Lock
 2 import time
 3 
 4 n = 100
 5 
 6 def task():
 7     global n
 8     mutex.acquire()
 9     temp = n
10     time.sleep(0.1)
11     n = temp - 1
12     mutex.release()
13 
14 if __name__ == "__main__":
15     mutex = Lock()
16     t_l = []
17     for i in range(100):
18         t = Thread(target=task)
19         t_l.append(t)
20         t.start()
21 
22     for t in t_l:
23         t.join()
24 
25     print('主:',n)
26 """
27 主 99   原因: 100个线程 都拿到了100  所以就是 100个线程100-1  数据不安全 效率高但是不安全
28           要将并行改为串行
29 """
30 """
31 主 0    原因:效率低了 但是数据安全了
32 """
互斥锁
6.GIL(global interpreter lock):
GIL本质就是一把互斥锁;
在一个python的进程内,不仅有test.py的主线程或者由该主线程开启的其他线程,还有解释器开启的垃圾回收等解释器级别的线程,总之,所有线程都运行在这一个进程内,毫无疑问.

1、所有数据都是共享的,这其中,代码作为一种数据也是被所有线程共享的(test.py的所有代码以及Cpython解释器的所有代码)
例如:test.py定义一个函数work(代码内容如下图),在进程内所有线程都能访问到work的代码,于是我们可以开启三个线程然后target都指向该代码,能访问到意味着就是可以执行。
2、所有线程的任务,都需要将任务的代码当做参数传给解释器的代码去执行,即所有的线程要想运行自己的任务,首先需要解决的是能够访问到解释器的代码。

执行流程:
target=task
多个线程先访问到解释器的代码,即拿到执行权限,然后将target的代码交给解释器的代码去执行
解释器的代码是所有线程共享的,所以垃圾回收线程也可能访问到解释器的代码而去执行,这就导致了一个问题:对于同一个数据100,可能线程1执行x=100的同时,而垃圾回收执行的是回收100的操作,解决这种问题没有什么高明的方法,就是加锁处理,如下图的GIL,保证python解释器同一时间只能执行一个任务的代码

7.GIL与Lock:
GIL:解释器级别的锁(保护的是解释器级别的数据,比如垃圾回收的数据)
Lock:应用程序的锁(保护用户自己开发的应用程序的数据)

   分析:
1、100个线程去抢GIL锁,即抢执行权限
2、肯定有一个线程先抢到GIL(暂且称为线程1),然后开始执行,一旦执行就会拿到lock.acquire()
3、极有可能线程1还未运行完毕,就有另外一个线程2抢到GIL,然后开始运行,但线程2发现互斥锁lock还未被线程1释放,于是阻塞,被迫交出执行权限,即释放GIL
4、直到线程1重新抢到GIL,开始从上次暂停的位置继续执行,直到正常释放互斥锁lock,然后其他的线程再重复2 3 4的过程
8.GIL与多线程: 
1.python的进程可以利用多核,但是开销大;
2.python的多线程开销小,但却无法利用多核的优势;(在python中一个进程中同一时刻只有一个线程执行用不上多核,有GIL锁)
共识:
1、cpu到底是用来做计算的,还是用来做I/O的? 计算的
2、多cpu,意味着可以有多个核并行完成计算,所以多核提升的是计算性能。
3、每个cpu一旦遇到I/O阻塞,仍然需要等待,所以多核对I/O操作没什么用处。
结论:
1、对计算来说,cpu越多越好,但是对于I/O来说,再多的cpu也没用
2、当然对运行一个程序来说,随着cpu的增多执行效率肯定会有所提高(不管提高幅度多大,总会有所提高),这是因为一个程序基本上不会是纯计算或者纯I/O,
所以我们只能相对的去看一个程序到底是计算密集型还是I/O密集型,从而进一步分析python的多线程到底有无用武之地
假设:
有四个任务要处理:玩出并发的效果;
方案:
方案一:开启四个进程
方案二:一个进程下,开启四个线程
单核:
如果四个任务是计算密集型,没有多核来并行计算,方案一徒增了创建进程的开销,方案二胜
如果四个任务是I/O密集型,方案一创建进程的开销大,且进程的切换速度远不如线程,方案二胜
多核:
如果四个任务是计算密集型,多核意味着并行计算,在python中一个进程中同一时刻只有一个线程执行用不上多核,方案一胜
如果四个任务是I/O密集型,再多的核也解决不了I/O问题,方案二胜
结论:
现在的计算机基本上都是多核,python对于计算密集型的任务开多线程的效率并不能带来多大性能上的提升,甚至不如串行(没有大量切换),
但是,对于IO密集型的任务效率还是有显著提升的。
应用:
多线程用于IO密集型,如:socket,爬虫,web
多进程用于计算密集型,如金融分析

# 如果并发的多个任务是计算密集型:多进程效率高
# 如果并发的多个任务是I/O密集型:多线程效率高
 1 # 如果并发的多个任务是计算密集型:多进程效率高
 2 from multiprocessing import Process
 3 from threading import Thread
 4 import os,time
 5 
 6 def task():
 7     res = 1
 8     for i in range(100000000):
 9         res*=i
10 
11 if __name__ == "__main__":
12     print('cpu:',os.cpu_count())
13     p_l = []
14     start = time.time()
15     for i in range(5):
16         p = Process(target=task)  # 耗时5s 多
17         # p = Thread(target=task)  # 耗时18s 多 多进程有gil锁的存在
18         p_l.append(p)
19         p.start()
20 
21     for p in p_l:
22         p.join()
23     stop = time.time()
24     print('run time:',stop-start)
25 
26 # 如果并发的多个任务是I/O密集型:多线程效率高
27 from multiprocessing import Process
28 from threading import Thread
29 import os,time
30 
31 def task():
32     time.sleep(2)
33     print('--->')
34 
35 if __name__ == "__main__":
36     p_l = []
37     print(os.cpu_count())  # 本机为8 核
38     start = time.time()
39     for i in range(400):
40         # p = Process(target=task)  # 耗时7s 多,大部分时间耗费在创建进程上面
41         p = Thread(target=task)  # 耗时2s多
42         p_l.append(p)
43         p.start()
44     for p in p_l:
45         p.join()
46     stop = time.time()
47     print('run time:',stop-start)
计算密集型/IO密集型
9.死锁与递归锁:
死锁:两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程
mutexA = Lock() (互斥锁)
mutexB = Lock() # 两个任务互相拿着锁,都没有释放 ,解决办法:递归锁
一个 acquire() 一个 release()
递归锁:
RLock = Lock + counter
(counter记录了acquire的次数,可以被多次acquire,直到一个线程所有的acquire都被release,其他的线程才能获得资源)
区别:
互斥锁:Lock() 只能被acquire一次
递归锁:RLock() 可以连续acquire多次
 1 from threading import Thread,Lock
 2 import time
 3 
 4 mutexA = Lock()
 5 mutexB = Lock()
 6 
 7 class MyThread(Thread):
 8     def run(self):
 9         self.func1()
10         self.func2()
11 
12     def func1(self):
13         mutexA.acquire()
14         print('拿到A锁',self.name)
15         mutexB.acquire()
16         print('拿到B锁',self.name)
17         mutexB.release()
18         mutexA.release()
19 
20     def func2(self):
21         mutexB.acquire()
22         print('拿到B锁',self.name)
23         time.sleep(2)
24         mutexA.acquire()
25         print('拿到A锁',self.name)
26         mutexA.release()
27         mutexB.release()
28 
29 if __name__ == "__main__":
30     for i in range(3):
31         p = MyThread()
32         p.start()
33 """
34 拿到A锁 Thread-1
35 拿到B锁 Thread-1
36 拿到B锁 Thread-1
37 拿到A锁 Thread-2   # 出现死锁,整个程序阻塞住
38 """
39 
40 from threading import Thread,RLock
41 import time
42 
43 mutexA = mutexB = RLock()
44 #一个线程拿到锁,counter加1,该线程内又碰到加锁的情况,则counter继续加1,这期间所有其他线程都只能等待,
45 # 等待该线程释放所有锁,即counter递减到0为止
46 class MyThread(Thread):
47     def run(self):
48         self.func1()
49         self.func2()
50 
51     def func1(self):
52         mutexA.acquire()
53         print('拿到A锁',self.name)
54         mutexB.acquire()
55         print('拿到B锁',self.name)
56         mutexB.release()
57         mutexA.release()
58 
59     def func2(self):
60         mutexB.acquire()
61         print('拿到B锁',self.name)
62         time.sleep(2)
63         mutexA.acquire()
64         print('拿到A锁',self.name)
65         mutexA.release()
66         mutexB.release()
67 
68 if __name__ == "__main__":
69     for i in range(3):
70         p = MyThread()
71         p.start()
72 """
73 拿到A锁 Thread-1
74 拿到B锁 Thread-1
75 拿到B锁 Thread-1
76 拿到A锁 Thread-1
77 拿到A锁 Thread-2
78 拿到B锁 Thread-2
79 拿到B锁 Thread-2
80 拿到A锁 Thread-2
81 拿到A锁 Thread-3
82 拿到B锁 Thread-3
83 拿到B锁 Thread-3
84 拿到A锁 Thread-3
85 """
死锁与递归锁
10.信号量:
信号量也是一把锁,可以指定信号量为5,对比互斥锁同一时间只能有一个任务抢到锁去执行,
信号量同一时间可以有5个任务拿到锁去执行。
sm = Semaphore(5)
sm.acquire()
sm.release()
解析:
Semaphore管理一个内置的计数器,
每当调用acquire()时内置计数器-1;
调用release() 时内置计数器+1;
计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。
 1 from threading import currentThread,Thread,Semaphore
 2 import time
 3 
 4 def task():
 5     sm.acquire()
 6     print('%s get sm'%currentThread().name)
 7     time.sleep(3)
 8     print('%s is done'%currentThread().name)
 9     sm.release()
10 
11 if __name__ == "__main__":
12     sm = Semaphore(5)
13     for i in range(10):
14         p = Thread(target=task)
15         p.start()
信号量
11.Event事件:
其他线程需要通过判断某个线程的状态来确定自己下一步的操作,使用Event事件。
多个线程之间同步的,一个线程告诉另一些线程可以做其他的活了
event = Event()
event.set()
event.wait()
event.wait(2)
event.is_set() # 默认是false,直到event.set()设为True
 1 from threading import Thread,Event
 2 import time
 3 
 4 def student(name):
 5     print('学生%s 正在听课'%name)
 6     event.clear()
 7     # event.wait()  # 学生要等7秒 才能下课
 8     event.wait(2)   # 学生等2秒 直接下课了
 9     print('学生%s 课间活动'%name)
10 
11 def teacher(name):
12     print('老师%s 正在授课'%name)
13     time.sleep(7)
14     event.set()
15 
16 if __name__ == "__main__":
17     event = Event()
18     stu1 = Thread(target=student,args=('alice',))
19     stu2 = Thread(target=student, args=('lily',))
20 
21     t1 = Thread(target=teacher,args=('egon',))
22     t1.start()
23     stu1.start()
24     stu2.start()
25 """
26 老师egon 正在授课
27 学生alice 正在听课
28 学生lily 正在听课
29 学生alice 课间活动
30 学生lily 课间活动
31 """
32 # 设置链接的超时时间
33 from threading import Thread,Event,currentThread
34 import time
35 
36 def conn():
37     n = 0
38     while not event.is_set():
39         if n == 3:
40             print('%s try too many times'%currentThread().name)
41             return
42         print('%s try,n:%s'%(currentThread().name,n))
43         event.wait(2)
44         n += 1
45     print('%s is connected'%currentThread().getName())
46 
47 def check():
48     print('%s is checking'%currentThread().getName())
49     time.sleep(3)
50     event.set()
51 
52 if __name__ == "__main__":
53     event = Event()
54     for i in range(3):
55         p = Thread(target=conn)
56         p.start()
57     t = Thread(target=check)
58     t.start()
59 """
60 Thread-1 try,n:0
61 Thread-2 try,n:0
62 Thread-3 try,n:0
63 Thread-4 is checking
64 Thread-1 try,n:1
65 Thread-3 try,n:1
66 Thread-2 try,n:1
67 Thread-1 try,n:2
68 Thread-2 try,n:2
69 Thread-3 try,n:2
70 Thread-1 try too many times
71 Thread-2 try too many times
72 Thread-3 try too many times
73 """
Event事件
12.定时器 Timer
指定n秒后执行某操作
t = Timer(5,task,args=('alice',))
t.start()
t.cancel()
 1 from threading import Timer
 2 
 3 def task(name):
 4     print('%s say hello'%name)
 5 
 6 t = Timer(5,task,args=('alice',))  # 5s 后执行这个函数
 7 t.start()
 8 # t.cancel()  # 取消
 9 
10 # 定时输入一个验证码:
11 from threading import Timer
12 import random
13 import string
14 
15 class code:
16     def __init__(self):
17         self.make_cache()
18 
19     def make_cache(self,interval=4):
20         self.cache = self.make_code()
21         print(self.cache)
22         self.t = Timer(interval,self.make_cache)
23         self.t.start()
24 
25     def make_code(self):
26         res = ''.join(random.sample(string.digits+string.ascii_lowercase,4))
27         return res
28 
29     def check(self):
30         while True:
31             code = input('>>>:').strip()
32             if code == self.cache:
33                 print('输入正确')
34                 self.t.cancel()
35                 break
36 
37 obj = code()
38 obj.check()
定时器 Timer
13.线程queue:
queue is especially useful in threaded programming when information must be exchanged safely between multiple threads.
有3种用法:
1.队列:先进先出
q = queue.Queue(2)
q.put('first)
q.put(5,block=False) # 不阻塞,抛出异常
q.put_nowait(5) # 不阻塞,抛出异常
q.put(5,block=True,timeout=3) # 阻塞,等待3s 抛出异常
q.get()
q.get(block=False) # 不阻塞,抛出异常
q.get_nowait() # 不阻塞,抛出异常
q.get(block=True,timeout=3) # 阻塞,等待3s 抛出异常
2.堆栈:后进先出 last in first out
q = queue.LifoQueue(3)
q.put('first')
q.get()
3.优先级队列:存储数据时可设置优先级的队列,数字越小,优先级越高
q = queue.PriorityQueue(3)
q.put((10,{'name':'alice'}))
q.get()
 1 import queue
 2 q = queue.Queue(4)
 3 q.put('first')
 4 q.put(2)
 5 q.put('third')
 6 q.put(4)
 7 # q.put(5)
 8 # q.put(5,block=False)  # 不阻塞,抛出异常
 9 # q.put(5,block=True,timeout=3) # 阻塞,等待3s 抛出异常
10 # q.put_nowait(5)  # 不阻塞,直接抛出异常
11 
12 print(q.get())
13 print(q.get())
14 print(q.get())
15 print(q.get())
16 # print(q.get())
17 # print(q.get(block=False))  # 不阻塞,直接抛出异常
18 # print(q.get_nowait())      # 不阻塞,直接抛出异常
19 print(q.get(block=True,timeout=3))  # 阻塞,等待3s 抛出异常
先进先出 队列
 1 import queue
 2 
 3 q = queue.LifoQueue(3)
 4 q.put('first')
 5 q.put(2)
 6 q.put('third')
 7 print(q.get())
 8 print(q.get())
 9 print(q.get())
10 """
11 third
12 2
13 first
14 """
后进先出 堆栈
 1 import queue
 2 
 3 q = queue.PriorityQueue(3)
 4 q.put((10,{'name':'alice'}))  # 元组的 形式
 5 q.put((40,'two'))
 6 q.put((20,4))
 7 print(q.get())
 8 print(q.get())
 9 print(q.get())
10 """
11 (10, {'name': 'alice'}) # 数字越小,优先级越高
12 (20, 4)
13 (40, 'two')
14 """
优先级 队列
14.进程池线程池:
对服务端开启的进程数或线程数加以控制,让机器在一个自己可以承受的范围内运行,这就是进程池或线程池的用途.
本质还是基于多进程,只不过是对开启进程的数目加上了限制.
官网:https://docs.python.org/dev/library/concurrent.futures.html
pool = ProcessPoolExecutor(4)
pool = ThreadPoolExecutor(4)
pool.submit(task,'egon%s'%i)
pool.shutdown() # 默认wait=True 相当于 pool.close() + pool.join()
pool.shutdown(wait=False) # 不阻塞,立即返回,不会等池内的任务执行完毕
pool.map(task,range(1,12)) # map 取代了 for + submit
 1 from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
 2 import os,time,random
 3 
 4 def task(name):
 5     print('name:%s pid:%s run'%(name,os.getpid()))
 6     time.sleep(random.randint(1,3))
 7 
 8 if __name__ == '__main__':
 9     # pool = ProcessPoolExecutor(4)
10     pool = ThreadPoolExecutor(4)
11 
12     # futures = []
13     # for i in range(10):
14     #     future = pool.submit(task,'egon%s'%i)  # 异步提交任务
15     #     futures.append(future)
16 
17     pool.map(task,range(1,12))  # map 取代了 for + submit
18 
19     # pool.shutdown()  # 默认wait=True 相当于 pool.close() + pool.join()
20     pool.shutdown(wait=False) # 不阻塞,立即返回,不会等池内的任务执行完毕
21     #不管wait 参数为何值,整个程序都会等到所有任务执行完毕
22     print('')
23     #
24     # for future in futures:
25     #     print(future.result())
26 
27 """
28 name:egon0 pid:31288 run   # 进程池 只有4个不同的pid
29 name:egon1 pid:30360 run
30 name:egon2 pid:27532 run
31 name:egon3 pid:28008 run
32 
33 name:egon4 pid:31288 run
34 name:egon5 pid:27532 run
35 
36 name:egon6 pid:28008 run
37 
38 name:egon7 pid:31288 run
39 name:egon8 pid:30360 run
40 name:egon9 pid:28008 run
41 
42 43 """
44 """
45 name:egon0 pid:25788 run   # 线程池 只有1个pid
46 name:egon1 pid:25788 run
47 name:egon2 pid:25788 run
48 name:egon3 pid:25788 run
49 
50 name:egon4 pid:25788 run
51 
52 name:egon5 pid:25788 run
53 name:egon6 pid:25788 run
54 
55 name:egon7 pid:25788 run
56 name:egon8 pid:25788 run
57 name:egon9 pid:25788 run
58 
59 60 """
进程池线程池
15.异步调用 + 回调机制 
提交任务的两种方式:
同步调用:提交完任务后,就在原地等待任务执行完毕,拿到结果,再执行下一行代码,导致程序是串行执行,效率低
异步调用:提交完任务后,不等待任务执行完毕。异步调用+回调机制 自动触发叫回调
同步调用:
pool = ThreadPoolExecutor(5)
shit1 = pool.submit(la,'alex').result()
weight(shit1)
shit2 = pool.submit(la,'egon').result()
weight(shit2)
异步调用: 异步调用 + 回调机制
pool = ThreadPoolExecutor(5)
pool.submit(la,'alex').add_done_callback(weight)
 1 from concurrent.futures import ThreadPoolExecutor
 2 import time
 3 import random
 4 
 5 def la(name):
 6     print('%s is laing'%name)
 7     time.sleep(random.randint(3,5))
 8     res = random.randint(7,13)*'#'
 9     return {'name':name,'res':res}
10 
11 def weight(shit):
12     name = shit['name']
13     size = len(shit['res'])
14     print('%s 拉了 <%s>kg'%(name,size))
15 
16 if __name__ == "__main__":
17     pool = ThreadPoolExecutor(5)
18     shit1 = pool.submit(la,'alex').result()
19     weight(shit1)
20 
21     shit2 = pool.submit(la,'egon').result()
22     weight(shit2)
23 """
24 alex is laing
25 alex 拉了 <7>kg
26 egon is laing
27 egon 拉了 <11>kg
28 """
同步调用
 1 from concurrent.futures import ThreadPoolExecutor
 2 import time
 3 import random
 4 
 5 def la(name):
 6     print('%s is laing'%name)
 7     time.sleep(random.randint(3,5))
 8     res = random.randint(7,13)*'#'
 9     return {'name':name,'res':res}
10 
11 def weight(shit):
12     shit = shit.result()  # 传给它的是一个对象,需要result()
13     name = shit['name']
14     size = len(shit['res'])
15     print('%s 拉了 <%s>kg'%(name,size))
16 
17 if __name__ == "__main__":
18     pool = ThreadPoolExecutor(5)
19     pool.submit(la,'alex').add_done_callback(weight)
20     pool.submit(la,'egon').add_done_callback(weight)
21 """
22 alex is laing
23 egon is laing
24 alex 拉了 <7>kg
25 egon 拉了 <12>kg
26 """
异步调用+回调机制
16.进程池线程池练习:应用场景:爬虫 
pip3 install requests
 1 from concurrent.futures import ThreadPoolExecutor
 2 import requests
 3 import time
 4 
 5 def get(url):
 6     print('get %s'%url)
 7     response = requests.get(url)
 8     time.sleep(3)
 9     return {'url':url,'content':response.text}
10 
11 def parse(res):
12     res = res.result()
13     print('%s parse res is %s'%(res['url'],len(res['content'])))
14 
15 if __name__ == "__main__":
16     urls=[
17         'http://www.cnblogs.com/linhaifeng',
18         'https://www.python.org',
19         'https://www.openstack.org',
20     ]
21 
22     pool = ThreadPoolExecutor(3)
23     for url in urls:
24         pool.submit(get,url).add_done_callback(parse)
25 """
26 get http://www.cnblogs.com/linhaifeng
27 get https://www.python.org
28 get https://www.openstack.org
29 http://www.cnblogs.com/linhaifeng parse res is 15923
30 https://www.openstack.org parse res is 62363
31 https://www.python.org parse res is 49299
32 """
线程池+异步调用+回调
17.线程 + queue = 实现线程池的效果
 1 # -*- coding:utf-8 -*-
 2 import socket
 3 from threading import Thread
 4 import queue
 5 
 6 def task(conn):
 7     while True:
 8         try:
 9             data = conn.recv(1024)
10             conn_dir[conn] = data.upper()
11             if not data:
12                 q_conn.get()
13                 break
14             conn.send(conn_dir[conn])
15         except Exception:
16             q_conn.get()
17             break
18     conn.close()
19 
20 def server(ip,port):
21     server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
22     server.bind((ip,port))
23     server.listen(5)
24     while True:
25         conn,addr = server.accept()
26         t = Thread(target=task,args=(conn,))
27         q_conn.put(t)
28         t.start()
29     server.close()
30 
31 if __name__ == "__main__":
32     q_conn = queue.Queue(2)
33     conn_dir = {}
34     server('127.0.0.1',8080)
server
 1 # -*- coding:utf-8 -*-
 2 import socket
 3 
 4 client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 5 client.connect(('127.0.0.1',8080))
 6 while True:
 7     msg = input('>>>:').strip()
 8     if not msg:continue
 9     client.send(msg.encode('utf-8'))
10     data = client.recv(1024)
11     print(data.decode('utf-8'))
client
18.Condition
  某些事件触发或达到特定的条件后才处理数据,默认创建了一个lock对象。
con = threading.Condition()
con.acquire()
con.notify()
con.wait()
con.release()
 1 import threading
 2 import time
 3 L = []
 4 
 5 class boy(threading.Thread):
 6     def __init__(self, cond, name='A boy'):
 7         threading.Thread.__init__(self)
 8         self.cond = cond
 9         self.name = name
10 
11     def run(self):
12         time.sleep(1)
13         '''''boy start conversation, make sure
14            the girl thread stared before send notify'''
15         self.cond.acquire()
16         print(self.name + ':Hello pretty~,I miss you\n')
17         self.cond.notify()
18         self.cond.wait()
19         print(self.name + ':like moth missing fire\n')
20         self.cond.notify()
21         self.cond.wait()
22         print(self.name + ':and I bought a gift for you in the list L\n')
23         L.append('channel5')
24         self.cond.notify()
25         self.cond.release()
26 
27 
28 class girl(threading.Thread):
29     def __init__(self, cond, name='A girl'):
30         threading.Thread.__init__(self)
31         self.cond = cond
32         self.name = name
33 
34     def run(self):
35         self.cond.acquire()
36         self.cond.wait()
37         print(self.name + ':Really, show me how much~\n')
38         self.cond.notify()
39         self.cond.wait()
40         print(self.name + ':you\'re so sweet~')
41         self.cond.notify()
42         self.cond.wait()
43         print(self.name + ':wow~~, that\'s ' + L.pop() + '---the one I dreamed for so long, I love you')
44         self.cond.release()
45 
46 
47 if __name__ == '__main__':
48     cond = threading.Condition()
49     husband = boy(cond, 'Aidan')
50     wife = girl(cond, 'PPP')
51     husband.start()
52     wife.start()
53     # husband.start()
54     husband.join()  # wait untill these two threads end
55     wife.join()
56     print ('end converdarion\n')
57 
58 """
59 Aidan:Hello pretty~,I miss you
60 
61 PPP:Really, show me how much~
62 
63 Aidan:like moth missing fire
64 
65 PPP:you're so sweet~
66 Aidan:and I bought a gift for you in the list L
67 
68 PPP:wow~~, that's channel5---the one I dreamed for so long, I love you
69 end converdarion
70 """
案例一
 1 #coding=utf-8
 2 import threading
 3 import time
 4 import datetime
 5 num = 0
 6 con = threading.Condition()
 7 
 8 class Gov(threading.Thread):
 9     def __init__(self):
10         super(Gov, self).__init__()
11 
12     def run(self):
13         global num
14         con.acquire()
15         while True:
16             print ("开始拉升股市")
17             num += 1
18             print ("拉升了" + str(num) + "个点")
19             time.sleep(2)
20             if num == 5:
21                 print ("暂时安全!")
22                 con.notify()
23                 con.wait()
24         con.release()
25 
26 
27 class Consumers(threading.Thread):
28     def __init__(self):
29         super(Consumers, self).__init__()
30 
31     def run(self):
32         global num
33         con.acquire()
34         while True:
35             if num > 0:
36                 print ("开始打压股市")
37                 num -= 1
38                 print ("打压了" + str(num) + "个点")
39                 time.sleep(2)
40                 if num == 0:
41                     print ("你妹的!天台在哪里!")
42                     con.notify()
43                     con.wait()
44         con.release()
45 
46 if __name__ == '__main__':
47     p = Gov()
48     c = Consumers()
49     p.start()
50     c.start()
51 
52 """
53 开始拉升股市
54 拉升了1个点
55 开始拉升股市
56 拉升了2个点
57 开始拉升股市
58 拉升了3个点
59 开始拉升股市
60 拉升了4个点
61 开始拉升股市
62 拉升了5个点
63 暂时安全!
64 开始打压股市
65 打压了4个点
66 开始打压股市
67 打压了3个点
68 开始打压股市
69 打压了2个点
70 开始打压股市
71 打压了1个点
72 开始打压股市
73 打压了0个点
74 你妹的!天台在哪里!
75 开始拉升股市
76 拉升了1个点
77 开始拉升股市
78 拉升了2个点
79 """
案例二




posted @ 2018-04-03 14:03  Alice的小屋  阅读(230)  评论(0编辑  收藏  举报