8-2-4python语法基础-并发编程-进程和线程对比学习:进程通信和线程通信

前言

进程和线程,有很多地方非常类似,包括使用的方法也很多相同的,
所以我决定放到一起对比学习,
这一篇,专门对比:
进程通信
线程通信

进程间通信

多线程:共享变量很简单,直接定义全局 global 变量即可
多进程:全局变量在多个进程中不共享,进程之间的数据是独立的,默认情况下互不影响。

  • 进程之间的数据隔离问题,也就是不能共享全局变量
  • 进程和进程之间的数据肯定是隔离的,比如qq和微信,不隔离就坏事了
  • 几个进程之间,如果不通过特殊的手段,是不可能共享一个数据的,这个记住,没有什么可理解的,
from multiprocessing import Process
num=1
def work1():
    global num
    num+=5
    print('子进程1运行,num:',num)
 
def work2():
    global num
    num += 10
    print('子进程2运行,num:',num)
 
 
if __name__=='__main__':
    print('父进程开始运行')
    p1=Process(target=work1)
    p2=Process(target=work2)
    p1.start()
    p2.start()
    p1.join()
    p2.join()

执行结果

父进程开始运行
子进程1运行,num:6
子进程2运行,num:11

进程之间通信

场景:安卓ui自动化框架,使用的是多进程实现的多设备并行。而在捞取数据做数据汇总时,需要多进程可以数据共享。

进程之间通信1,---管道

  • 上面我们看到了,进程之间是不能通信的,那怎么解决进程之间的通信问题呢,可以使用管道,
    from multiprocessing import Pipe

进程之间通信2,---队列

  • 认知队列的操作
  • 1,主要是put和get,
  • 2,使用这个队列,可以实现复杂的生产者和消费者模型,这个将是主要的进程之间通信的方式,
from multiprocessing import Process, Queue, set_start_method
import time, random, os


def consumer(q):
    while True:
        res = q.get()
        if res is None:
            break  # 收到结束信号则结束
        time.sleep(random.randint(1, 3))
        print('\033[45m%s 吃 %s\033[0m' % (os.getpid(), res))


def producer(q):
    for i in range(10):
        time.sleep(random.randint(1, 3))
        res = '包子%s' % i
        q.put(res)
        print('\033[46m%s 生产了 %s\033[0m' % (os.getpid(), res))
    q.put(None)  # 发送结束信号


if __name__ == '__main__':
    set_start_method('fork')

    q = Queue()
    # 生产者们:即厨师们
    p1 = Process(target=producer, args=(q,))

    # 消费者们:即吃货们
    c1 = Process(target=consumer, args=(q,))

    # 开始
    p1.start()
    c1.start()
    print('进程间通信-队列-主进程')

进程之间通信3,---数据共享

共享数值型数据

主进程与子进程共用一个value

import multiprocessing
 
 
def func(num):
    num.value = 2  
 
 
if __name__ == "__main__":
    num = multiprocessing.Value("d", 1) 
    p = multiprocessing.Process(target=func, args=(num,))
    p.start()
    p.join()

共享数组型数据: 共享list实践

子进程改变dict,list,主进程dict,list跟着改变

进程间通讯有多种方式,包括信号,管道,消息队列,信号量,共享内存,socket等。这里主要介绍使用multiprocessing.Manager模块实现进程间共享数据。

Manager() 可以创建一个专门用来维护进程间数据共享的进程,它能提供 python 所支持的任何数据结构,也能用来实现进程间的数据通信。

Manager支持的类型有list,dict,Namespace,Lock,RLock,Semaphore,BoundedSemaphore,Condition,Event,Queue,Value和Array。

现在想把每个手机的自动化运行结果的元组拿出来分析,使用共享list的方式

多进程中,同一个变量,各自有一份拷贝存在于每个进程中, 互不影响;
多线程中,所有变量都由所有线程共享,任何一个变量都可以被任何一个线程修改.

import time
from multiprocessing import Process, Manager


def producer(m_list):
    print('producer producing goods start')
    for i in range(1, 6):
        m_list.append(f'good_{i}')
    print('producer producing goods end')
    

def consumer(m_list):
    while m_list:
        goods_name = m_list.pop()
        print(f'consumer consuming goods: {goods_name} start')
        time.sleep(1)
        print(f'consumer consuming goods: {goods_name} end')
        

if __name__ == "__main__":
    print('----主进程开始----')
    m_list = Manager().list()
    producer_process = Process(target=producer, args=(m_list,))
    consumer_process = Process(target=consumer, args=(m_list,))
    producer_process.start()
    consumer_process.start()
    producer_process.join()
    consumer_process.join()
    print('----主进程结束----')

这种生产者-消费者模型,使用pop的方式,不会设计到操作同一个数据的问题,
但是要知道如果使用这种数据共享,设计到操作同一个数据的问题,
要注意进程冲突的问题,很可能导致数据被错误修改,出现数据不一致的情况。
所以多个进程如果同时操作共享内存中的同一个资源时,需要加锁,且这里加的锁必须是 Manager 共享对象中的锁。

案例:

# coding=utf-8
from multiprocessing import Manager,Process

def task1(num, lock):
    for i in range(10000):
        with lock:
            num.value += 1

def task2(num, lock):
    for i in range(10000):
        with lock:
            num.value -= 1



if __name__ == "__main__":
    manager = Manager()    # 定义一个共享内存对象
    num = manager.Value("jaye", 7)
    lock = manager.Lock()  # 获取锁
    p1 = Process(target=task1, args=(num, lock))
    p2 = Process(target=task2, args=(num, lock))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print(f"主进程的num:{num.value}")   # 7


总结

  • 进程间的三种通信(IPC)方式:
  • 方式一:队列(推荐使用)
  • 方式二:管道(不推荐使用,了解即可)
  • 管道相当于队列,但是管道不自动加锁
  • 方式三:共享数据(不推荐使用,了解即可)
  • 共享数据也没有自动加锁的功能,所以还是推荐用队列的。感兴趣的可以研究研究管道和共享数据

记住进程之间,因为数据不同享,所以会有进程之间通信的问题,
但是线程不一样,线程之间可以共享全局变量,所以只要通过全局变量就可以实现线程的通信,
这一点要记住,

posted @ 2022-09-16 00:10  技术改变命运Andy  阅读(951)  评论(0编辑  收藏  举报