独立的进程内存空间与共享的服务器进程空间
进程间通信的限制
进程之间是互不干扰的独立内存空间
我们想的只是不能修改变量
但是,深层次问题是,这个进程与那个进程完全失去了联系
from multiprocessing import Process a =1 def func(): global a a = 2 if __name__ == '__main__': p = Process(target=func) p.start() p.join() print(a) >> 1
子进程修改数据对父进程没有影响
进程间通信的解决方案
在服务器进程中开辟一个公共空间,并返回一个代理,可以让多个进程访问。
Manger对象的基本使用
python中使用Manager来生成公共空间
一般常用的空间类型是:
- mgr.list()
- mgr.dict()
- mgr.Queue()
线程间共享的全局变量与同步锁的基本概念
线程间全局变量的共享
使用线程再来完成一下上面的案例
from threading import Thread a =1 def func(): global a a = 2 if __name__ == '__main__': t = Thread(target=func) t.start() t.join() print(a) >> 2
为什么使用线程不一样了呢
因为线程属于同一个进程,因此它们之间共享内存区域。因此全局变量是公共的。所以这样会带来什么问题呢?假设两个线程都在修改一个变量,那么修改完后这个变量的值应该是什么呢
扫描二维码关注公众号,回复:
2202138 查看本文章
共享内存间存在竞争问题
from threading import Thread a = 0 n = 1000000 def incr(n): global a for i in range(n): a += 1 #对a 进行n次加1 def decr(n): global a for i in range(n): a -= 1 # 对a进行n次减1 if __name__ == '__main__': t1 = Thread(target=incr,args=(n,)) t2 = Thread(target=decr,args=(n,)) t1.start() t2.start() t1.join() t2.join() print(a)
>>
17974
再次执行
>>
70664
我们理所当然的认为结果应当为0,但是事实并非如此。
如果在线程1获取a的值时,a为0,然后对a进行加1操作,此时还没有重新对a赋值,线程2又去取了a的值(此时仍为0)然后进行加1操作,最后赋值给a,当出现这种情况时,执行两次加1操作,a最后的结果却为1。所以上面的例子,结果并不一定为0
使用锁来控制共享资源的访问
下面用两种方式对上面的例子进行修改:
如果线程1申请了锁,但是还未释放锁,而线程2此时要申请锁,这个时候将处于阻塞状态,当线程1释放锁,线程2才会继续执行。
线程与进程安全的队列
队列的基本概念
一个入口,一个出口 先入先出(FIFO)
线程安全或进程安全的队列中已经帮我们实现了锁。
因此我们不需要再自己使用锁来同步。
线程安全队列操作一览
q = queue.Queue(maxsize=0) # 构造一个先进显出队列,maxsize指定队列长度,为0 时,表示队列长度无限制。 q.join() # 等到队列为空的时候,在执行别的操作 q.qsize() # 返回队列的大小 (不可靠) q.empty() # 当队列为空的时候,返回True 否则返回False (不可靠) q.full() # 当队列满的时候,返回True,否则返回False (不可靠) q.put(item, block=True, timeout=None) # 表示往队列里面放,当队列已满的时候,会处于阻塞 q.get(block=True, timeout=None) # 表示从队列里面取值,如果队列里面没值是会一直等即阻塞
q.put_nowait(item) # 等效于 put(item,block=False) q.get_nowait() # 等效于 get(item,block=False) 表示如果队列里面没值时不等
进程的队列
from multiprocessing import Queue queue = Queue() # 进程队列的实例
线程的队列
import queue queue = queue.Queue() #线程队列的实例
例子
from multiprocessing import Queue #导入Queue方法实现队列 q=Queue(3) #创建队列的类 3是允许最大的数量。不填写等于无限制 q.put('first') #往队列中添加数据,可以添加任何类型的数据 q.put('second') q.put('third') # q.put('fourth') 因为最大只能添加三个消息。所以在存放第四个的时候会等待第一个消息取出后才会存放进去。 # print(q.get()) #取值 先进先出原则,所以第一个去出的事first print(q.get()) print(q.get())
消费者与生产者模式
所谓,生产者与消费者模型,其实是把一个需要进程通信的问题
分开考虑
生产者,只需要往队列里面丢东西(生产者不需要关心消费者)
消费者,只需要从队列里面拿东西(消费者也不需要关心生产者)
Web服务器与Web框架之间的关系
生产者:
只关心队列是否已满,没满,则生产,满了就阻塞。
消费者:
只关心队列是否为空,不为空,则消费,为空则阻塞。
例子:
1 import time 2 import queue 3 import random 4 from threading import Thread 5 6 class Productor(Thread):#生产者 7 def __init__(self,queue): 8 super().__init__() 9 self.queue = queue 10 11 def get_lottery_num(self): #彩票生成方法 12 red_num_list = list(range(1,34))#红球所有可能的数字 13 blue_num_list = list(range(1,17))#篮球所有可能的数字 14 random.shuffle(red_num_list)#随机打乱数字顺序 15 red_num = sorted(red_num_list[:6])#获取随机的6个红球并排序 16 blue_num = random.choice(blue_num_list)#随机获取一个蓝球 17 lottery_num = {'红球':red_num,'蓝球':blue_num} 18 return lottery_num 19 20 def run(self): 21 while True: 22 if self.queue.full(): 23 print('已生产足够多的彩票等待出售!') 24 self.queue.task_done() 25 time.sleep(2)#等待两秒,让消费者购买一些彩票 26 27 elif self.queue.qsize() > 12: 28 print('有很多彩票等待出售!') 29 time.sleep(1)#等待一秒,让消费者购买一些彩票 30 else: 31 self.queue.put(self.get_lottery_num()) 32 print('生产一张彩票!') 33 34 class Consumer(Thread):#消费者 35 def __init__(self,queue): 36 super().__init__() 37 self.queue = queue 38 39 def run(self): 40 while True: 41 if self.queue.empty(): 42 print('没有足够多的彩票,请等待!') 43 time.sleep(2)#等待两秒,让生产者生产一些彩票 44 elif self.queue.qsize() < 5: 45 print('彩票快没了!') 46 time.sleep(1)#等待一秒,让生产者生产一些彩票 47 else: 48 self.queue.get() 49 print('出售一张彩票!') 50 51 52 if __name__ == '__main__': 53 q = queue.Queue(20) 54 p = Productor(q) 55 c = Consumer(q) 56 p.start() 57 c.start() 58 p.join() 59 c.join() 60 q.join()