DAY 34 进程通信、消费者模型和线程

一.进程间通行

  队列:先进先出

  堆栈:先进后出

  一般利用队列实现进程间的通信

from multiprocessing import Queue
    q = Queue(5) # 产生一个最多能够存放五个数据的队列

    q.put(1) # 往队列中存放数据,
             # 如果存放的数据个数大于队列最大存储个数,程序会阻塞 

    print(q.full) # 判断队列是否饱和
    
    q.get() # 取数据,get一次就取一个

    q.get_nowait() # 在队列有数据的情况下,与get取值相同
                   # 在队列没有数据的情况下,取值直接报错

    print(q.empty()) # 判断队列是否为空,在并发情况下,判断不准确

    print(q.get()) # 如果队列为空,get就会在原地等待队列中有数据过来

二.基于队列实现进程间通信

from multiprocessing import Queue, Process
    
def producer(q):
    q.put('hello world')


def consumer(q):
    print(q.get())


if __name__ =='__main__':
    q.Queue() # 生成一个队列对象
    p1 = Process(target=producer,args=(q,))
    c1 = Process(target=consumer,args=(q,))
    p1.start()
    c1.start()

三.生产者消费者模型

  生产者消费者模型:

    生产者: 生产数据的程序

    消费者: 处理数据的程序

  解决供需不平衡的问题

    定义一个队列,用来存放固定数量的数据

    解决一个生产者和消费者不需要直接交互,两者通过队列进行数据传输

  Queue(队列) = 管道 + 锁

  1.Queue方法

from multiprocessing import Process,Queue,JoinableQueue
import time
import random

def produce(name,food,q):
    for i in range(5):
        data = '%s 做了 %s%s' % (name,food,i)
        time.sleep(random.randint(1,3))
        print(data)
        q.put(data) # 将生产的数据放入队列中


def consumer(name,q):
    while True:
        data:str = q.get() 
        if data is None:break
        time.sleep(random.randint(1,3))
        data = data.replace('', '')
        print('%s 吃了 %s' % (name, data))


if __name__ == '__main__':
    q = Queue() # 生成一个队列对象
    p1 = Process(target=produce,args=('egon', '包子', q,))
    p2 = Process(target=produce,args=('tank', '小笼', q,))
    c1 = Process(target=consumer,args=('owen', q,))
    c2 = Process(target=consumer,args=('jason', q,))
    p1.start()
    p2.start()
    c1.start()
    c2.start()
    p1.join() # 等待生产者生产完所有的数据
    p2.join() # 等待生产者生产完所有的数据
    # 在生产者生产完数据之后,往队列里面放一个提示性的消息,告诉消费者已经没有数据,你走吧,不要等了
    #q.put(None)
    #q.put(None)
  
  

2.JoinableQueue方法

from multiprocessing import Process, JoinableQueue
import time
import random


def produce(name, food, q):
    for i in range(5):
        data = '%s 做了 %s%s' % (name, food, i)
        time.sleep(random.randint(1, 3))
        print(data)
        q.put(data)  # 将生产的数据放入队列中


def consumer(name, q):
    while True:
        data: str = q.get()
        if data is None: break
        time.sleep(random.randint(1, 3))
        data = data.replace('', '')
        print('%s 吃了 %s' % (name, data))
        q.task_done()  # 告诉你的队列,你已经将数据取出并且处理完毕


if __name__ == '__main__':
    q = JoinableQueue()  # 生成一个队列对象
    p1 = Process(target=produce, args=('egon', '包子', q,))
    p2 = Process(target=produce, args=('tank', '小笼', q,))
    c1 = Process(target=consumer, args=('owen', q,))
    c2 = Process(target=consumer, args=('jason', q,))
    p1.start()
    p2.start()
    c1.daemon = True
    c2.daemon = True
    c1.start()
    c2.start()
    # 等待生产者生产完所有的数据
    p1.join()
    p2.join()
    q.join()  # 等待队列中数据全部取出

四.线程

  1.什么是线程

    进程:资源单位(只申请内存空间)

    线程:执行单位(执行代码)

    注意: 每一个进程中都会自带一个线程

  2.为什么要有线程

    开启一个进程:

      申请内存空间 耗时

      将代码拷贝到申请的内存空间中 耗时

    开启一个线程

      不需要申请内存空间 速度快

      不需要拷贝代码 省时  

    总结:开线程的开销远远小于进程的开销!!!

五.开启线程的两种方式

  1.Thread模块开始线程

from threading import Thread
import time


def task(name):
    print('%s is running' % name)
    time.sleep(1)
    print('%s is running' % name)


t = Thread(target=task,args=('egon',))
t.start()
print('')

2.自定义模块继承Thread模块开始线程

from threading import Thread
import time


class MyThread(Thread):
    def __init__(self,name):
        super().__init__() # 重写功能,以防父类功能缺少执行
        self.name = name

    def run(self):
        print('%s is running' % self.name)
        time.sleep(1)
        print('%s is running' % self.name)


t = MyThread('egon')
t.start()
print('')

六.线程之间数据共享

join只能将整体进行串行,人为的规定顺序。

from threading import Thread

x = 100


def task():
    global x
    x = 1


t = Thread(target=task)
t.start()
t.join()
print(x)

七.线程互斥锁

互斥锁同一时间只有一个线程能运行,把并行变成串行,为了保证数据的安全性。从而降低了效率。

互斥锁可以将局部变成串行让大家公平的抢

from threading import Thread, Lock
import time

x = 100
mutex = Lock()


def task():
    global x
    mutex.acquire()
    temp = x
    time.sleep(0.1)
    x = temp - 1
    mutex.release()


t_list = []
for i in range(100):
    t = Thread(target=task)
    t_list.append(t)
    t.start()

for i in t_list:
    i.join()

print(x)

八.线程对象其他属性和方法

from threading import Thread, active_count, current_thread
import os, time


def task():
    print('%s is running' % current_thread().name,os.getpid())
    time.sleep(1)
    print('%s is done' % os.getpid())


t = Thread(target=task)
t.start()
print(active_count())
print('', os.getpid(),current_thread().name)

九.守护线程

大前提:一个任务守护另外一个任务代码的执行过程

在一个进程内只有一个线程的情况下,守护进程会在主进程代码执行完毕后立刻结束

在一个进程可以开启多个线程,守护的线程会在该进程内所有非守护线程都执行完之后才结束

from threading import Thread
import time


def task():
    print('is running')
    time.sleep(2)
    print('is over')


t = Thread(target=task)
t.daemon = True
t.start()
print('')
原文地址:https://www.cnblogs.com/zhengyuli/p/10828141.html