No.34生产者消费者模型
No.34
今日概要
- 生产者消费者模型
- JoinableQueue
- 进程之间的数据共享
- 初识线程
- threading 模块
- 测试
内容回顾
-
tcp协议:
- 可靠:被拦截会重发,不会丢数据。
- 不安全:被拦截的数据别人能看懂。
-
迭代器:
-
只能取一次值
- for
- list
__next__
-
不取值不执行
ret = filter(lambda n:n%3==0, range(10)) # ret是filter对象,有__iter__方法,也有__next__方法。 # 既是可迭代对象,也是迭代器。 print(ret) # list(ret) 通过强制转换取迭代器中的值 [0, 3, 6, 9] print(len(list(ret))) 结果:4 # 迭代器中的值被取一次之后就没有了 [] print(list(ret)) 结果:0def demo(): for i in range(4): yield i g = demo() # g是生成器函数也是特殊的迭代器 g1 = (i for i in g) # g1是生成器 g2 = (i for i in g1) # g2是生成器 print(list(g1)) # 强制转换取值 结果:[0,1,2,3] print(list(g2)) # g1值被取走一次后就没有了 结果:[ ]def add(n, i): return n+i def test(): for i in range(4): yield i g = test() # 生成器 for n in [1, 10]: # for循环给g赋了两次值 g=(add(n,i) for i in g) # g是生成器 ''' n=1 g=(add(1,i) for i in test()) n=10 g=(add(10,i) for i in (add(10,i) for i in test)) ''' print(list(g)) 结果: [20, 21, 22, 23]
-
-
IO操作
- I → input 向内存输入
- input / read / recv / recvfrom / accept / connect / close
- O → ouptut 从内存输出
- print / write / send / sendto / accept / connect / close
- I → input 向内存输入
-
start / terminate / join
- start / terminate: 异步非阻塞
- join:同步阻塞
-
IPC
- 进程之间通信
- 内置ipc机制:
- 队列
- 管道
- 第三方工具提供的ipc机制:
- redic
- memcache
- kafka
- rabbitmq
内容详细
1.生产者消费者模型
解耦:把写在一起的大功能分开写成多个小功能。
import time
import random
from multiprocessing import Process, Queue
def producer(q, name, food):
for i in range(1, 11):
time.sleep(random.random())
fd = '%s%s' % (food, i)
q.put(fd)
print('%s生产了一个%s' % (name, food))
def consumer(q, name):
while True:
food = q.get()
if not food:
q.put(None)
break
time.sleep(random.randint(1, 3))
print('%s吃了%s' % (name, food))
def pc(p_count, c_count):
q = Queue(10)
p_lst = []
for i in range(p_count):
p = Process(target=producer, args=(q, 'alex', '猪蹄'))
p.start()
p_lst.append(p)
for i in range(c_count):
c = Process(target=consumer, args=(q, 'wusir'))
c.start()
for p in p_lst:
p.join()
for i in range(c_count):
q.put(None)
if __name__ == '__main__':
pc(2,3)
2.Joinablequeue
import time
import random
from multiprocessing import Process, JoinableQueue
def producer(jq, name, food):
for i in range(1, 11):
time.sleep(random.random())
fd = '%s%s' % (food, i)
jq.put(fd) # 每次put一次数据之后,序列进行count+1计数
print('%s生产了一个%s' % (name, food))
jq.join() # put的数据全部消费完后才结束阻塞
def consumer(jq, name):
while True:
food = jq.get()
time.sleep(random.randint(1, 3))
print('%s吃了%s' % (name, food))
jq.task_done() # 每次get到的数据处理结束后,序列进行count-1计数
if __name__ == '__main__':
jq = JoinableQueue()
p = Process(target=producer, args=(jq, 'alex', '猪蹄'))
p.start()
c = Process(target=consumer, args=(jq, 'eric'))
c.daemon = True
c.start()
p.join()
3.进程之间的数据共享
from multiprocessing import Manager, Process, Lock
def func(dic, lock):
with lock:
dic['count'] -= 1
if __name__ == '__main__':
m = Manager()
lock = Lock() # 不加锁数据不安全,可能多个进程拿到同一个数剧同时修改。
dic = m.dict({'count': 60}) # 多个进程共享的字典。
lst = []
for i in range(60):
p = Process(target=func, args=(dic, lock))
p.start()
lst.append(p)
for p in lst:
p.join()
print(dic)
# multiprocessing中有一个Manager类
# 封装了所有和进程相关的
# 数据共享和数据传递的对象
# 但是对于字典、列表这一类对象操作的时候会产生数据不安全问题
# 需要通过加锁解决,建议尽量少的使用这种方式
4.初识线程
线程和进程的区别
- 线程:
- 开销小、数据共享、最小的CPU调度单位。
- 线程是进程的一部分。
- 进程:
- 开销大、数据隔离、最小的资源分配单位。
cpython解释器无法实现多线程并行(同时间点利用多核)
-
GIL 全局解释器锁
- 保证了整个python程序中,只能有一个线程被CPU执行。
- 加锁原因:cpython解释器中特殊的垃圾回收机制。
-
gc 垃圾回收机制
- 专门由解释器的一条线程来完成垃圾回收机制,对每一个在程序中的变量统计引用计数。
- 如果python程序中的其它线程和该线程并行,那么该线程就无法统计与之并行线程中变量的引用计数。
-
Python解释器
- cpython
- 有GIL锁,无法利用多核。
- pypy
- 有GIL锁,无法利用多核。
- jpython 可以利用多核
- ironpython 可以利用多核
- cpython
-
总结
- 虽然GIL锁导致线程无法实现并行,但不影响高IO操作程序的效率,只会影响高计算操作程序的效率。
- 遇到高计算操作解决方式
- 多进程 + 多线程
- 分布式
5.threading 模块
开启线程
import os
from threading import Thread
# multiprocessing 是完全仿照着 threading 写的
def func():
print(os.getppid())
Thread(target=func,).start() # 开启线程的速度非常的快
print('--->',os.getppid())
结果:
8420 # 子线程先打印,由此可见开启线程的效率远高于开启进程。
---> 8420
# 异步
import os
import time
from threading import Thread
def func():
print('start son thread')
time.sleep(1)
print('end son thread',os.getppid())
Thread(target=func,).start()
print('start')
time.sleep(0.5)
print('end',os.getppid())
开启多个子线程
import os
import time
from threading import Thread
def func(i):
print('start son thread', i)
time.sleep(1)
print('end son thread', i, os.getppid(),)
for i in range(10):
Thread(target=func, args=(i,)).start()
join方法
主线程什么时候结束?
- 等待所有子线程结束之后才结束。
- 主线程如果结束了,主进程也就结束了。
import os
import time
from threading import Thread
def func(i):
print('start son thread', i)
time.sleep(1)
print('end son thread', i, os.getppid(),)
t = Thread(target=func, args=(1,))
t.start()
t.join() # 阻塞直到子线程执行结束才结束
print('子线程执行完毕')
import time
from threading import Thread
def func(i):
print('start son thread', i)
time.sleep(1)
print('end son thread', i)
lst = []
for i in range(10):
t = Thread(target=func, args=(i,))
t.start()
lst.append(t)
for t in lst:
t.join()
print('子线程执行完毕')
面向对象的方式启动线程
import time
from threading import Thread
class MyThread(Thread):
def __init__(self, i):
self.i = i
super().__init__()
def run(self):
print('start', self.i)
time.sleep(1)
print('end', self.i)
for i in range(10):
MyThread(i).start()
import time
from threading import Thread
class MyThread(Thread):
def __init__(self, i):
self.i = i
super().__init__()
def run(self):
print('start', self.ident) # 线程id
time.sleep(1)
print('end', self.i)
for i in range(10):
t = MyThread(i)
t.start()
print(t.ident) # 线程id
# current_thread()在哪一个线程中调用,得到的结果就是当前线程的对象(信息)。
from threading import current_thread, Thread
def func():
t = current_thread() # current_thread()是一个线程对象,相当于面向对象方式开启线程中的self。
print(t.ident)
t = Thread(target=func)
t.start()
print(current_thread().ident) # 主线程id
import time
from threading import Thread,current_thread,enumerate,active_count
def func():
t = current_thread()
print(t.ident)
time.sleep(1)
t = Thread(target=func)
t.start()
print(enumerate()) # 查看当前有多少线程对象,返回的是列表
print(active_count()) # 相当于len(enumerate())
结果:
17848
[<_MainThread(MainThread, started 16140)>, <Thread(Thread-1, started 17848)>]
2
补充:
- 主进程可以通过
terminate方法结束子进程 - 主线程没有方法结束子线程
6测试
-
进程和线程的效率差
import time from multiprocessing import Process from threading import Thread def func(a,b): c = a+b if __name__ == '__main__': start = time.time() p_lst = [] for i in range(100): p = Process(target=func, args=(i, i*2)) p.start() p_lst.append(p) for p in p_lst: p.join() print('process',time.time()-start) start = time.time() t_lst = [] for i in range(100): t = Thread(target=func, args=(i, i*2)) t.start() t_lst.append(p) for t in t_lst: t.join() print('thread',time.time()-start) 结果: process 2.306135892868042 thread 0.014492273330688477 # 开一百个进程和一百个线程,效率差两百倍。 # 一般情况开的进程个数为CPU个数的1到2倍。 -
数据隔离还是共享
from threading import Thread n = 100 def func(): global n # 不要在子线程里随便修改全局变量 n -= 1 lst = [] for i in range(100): t = Thread(target=func) t.start() lst.append(t) for t in lst: t.join() print(n) 结果: 0 -
守护线程
import time from threading import Thread def son1(): while True: print('in son1') time.sleep(0.5) def son2(): for i in range(5): time.sleep(1) t = Thread(target=son1) t.daemon = True t.start() Thread(target=son2).start() time.sleep(3) # 守护线程一直等到所有的非守护线程代码都结束之后才结束,除了守护主线程也会守护子线程。 # 守护进程只用等到主进程代码执行结束就结束。

浙公网安备 33010602011771号