并发通信、生产者与消费者模型

独立的进程内存空间与共享的服务器进程空间

进程间通信的限制

进程之间是互不干扰的独立内存空间
我们想的只是不能修改变量
但是,深层次问题是,这个进程与那个进程完全失去了联系

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()

猜你喜欢

转载自www.cnblogs.com/woaixuexi9999/p/9325847.html