Python(十) 进程、线程、协程篇

本节内容

    1、操作系统发展史
    2、进程、与线程区别
    3、Python GIL全局解释器锁
    4、线程
        1、语法
        2、join
        3、线程锁 Lock、RLock、信号量
        4、将线程变为守护进程
        5、Event事件
        6、queue队列
        7、生产者消费者模型
        8、Queue队列
        9、开发一个线程池
    5、进程
        1、语法
        2、进程间通讯
        3、进程池

    6、协程

一、操作系统发展史  

1.1 手工操作(无操作系统)

    1946年第一台计算机诞生--20世纪50年代中期,还未出现操作系统,计算机工作采用手工操作方式。
            
            程序员将对应于程序和数据的已穿孔的纸带(或卡片)装入输入机,然后启动输入机把程序和数据输入计算机内存,接着通过控制台开关启动程序针对数据运行;计算完毕,打印机输出计算结果;用户取走结果并卸下纸带(或卡片)后,才让下一个用户上机。
            
            手工操作方式两个特点:
                (1)用户独占全机。不会出现因资源已被其他用户占用而等待的现象,但资源的利用率低。
                (2)CPU 等待手工操作。CPU的利用不充分。
             20世纪50年代后期,出现人机矛盾:手工操作的慢速度和计算机的高速度之间形成了尖锐矛盾,手工操作方式已严重损害了系统资源的利用率(使资源利用率降为百分之几,甚至更低),不能容忍。唯一的解决办法:只有摆脱人的手工操作,实现作业的自动过渡。这样就出现了成批处理。

1.2 批处理系统

            批处理系统:加载在计算机上的一个系统软件,在它的控制下,计算机能够自动地、成批地处理一个或多个用户的作业(这作业包括程序、数据和命令
            
            联机批处理系统
            
                首先出现的是联机批处理系统,即作业的输入/输出由CPU来处理。
                主机与输入机之间增加一个存储设备——磁带,在运行于主机上的监督程序的自动控制下,计算机可自动完成:成批地把输入机上的用户作业读入磁带,依次把磁带上的用户作业读入主机内存并执行并把计算结果向输出机输出。完成了上一批作业后,监督程序又从输入机上输入另一批作业,保存在磁带上,并按上述步骤重复处理。
                
            脱机批处理系统
            
                为克服与缓解高速主机与慢速外设的矛盾,提高CPU的利用率,又引入了脱机批处理系统,即输入/输出脱离主机控制。
                这种方式的显著特征是:增加一台不与主机直接相连而专门用于与输入/输出设备打交道的卫星机。
                其功能是:
                (1)从输入机上读取用户作业并放到输入磁带上。
                (2)从输出磁带上读取执行结果并传给输出机。
                
                这样,主机不是直接与慢速的输入/输出设备打交道,而是与速度相对较快的磁带机发生关系,有效缓解了主机与设备的矛盾。主机与卫星机可并行工作,二者分工明确,可以充分发挥主机的高速计算能力。
                
                脱机批处理系统:20世纪60年代应用十分广泛,它极大缓解了人机矛盾及主机与外设的矛盾。IBM-7090/7094:配备的监督程序就是脱机批处理系统,是现代操作系统的原型。

                不足:每次主机内存中仅存放一道作业,每当它运行期间发出输入/输出(I/O)请求后,高速的CPU便处于等待低速的I/O完成状态,致使CPU空闲。

                为改善CPU的利用率,又引入了多道程序系统。

1.3 多道程序系统               

  多道程序设计技术

                    所谓多道程序设计技术,就是指允许多个程序同时进入内存并运行,即同时把多个程序放入内容,并允许它们交替在CPU中运行,它们共享系统中的各种硬、软件资源,当一道程序因I/O请求而暂停运行时,CPU便立即转去另一道程序。

  单道程序的运行过程

                    在A程序计算时,I/O空闲,A程序操作时,CPU空闲,(B程序也是同样);必须A工作完成后,B才能进入内存中开始工作,两者的串行的,全部完成共需时间=T1+T2               

  多道程序的运行过程

                    将A、B两道程序同时存放在内存中,它们在系统的控制下,可互相穿插、交替地在cpu上运行;当A程序因请求I/O操作而放弃CPU时,B程序就可占用Cpu不再空闲,而正进行A I/O操作的I/O设备也不空闲,显然,CUP和I/O设备都处于“忙”状态,大大提高了资源的利用率,从而也提高了系统的效率,A、B全部完成所需时间<<T1+T2。
                    
                    多道城西设计技术不仅使Cpu得到充分利用,同时改善I/O设备和内存的利用率,从而提高了整个系统的资源利用率和系统吞吐量(单位时间内处理作业(程序)的个数),最终提高了整个系统的效率。

  单处理机系统中多道城西运行时的特点:

                    (1)多道:计算机内存中同时存放几道相互独立的程序;
                    (2)宏观上并行:同时进入系统的几道程序都处于运行过程中,即它们先后开始了各自的运行,但都未运行完毕;
                    (3)微观上串行:实际上,各道程序轮流地用CPU,并交替运行。

  多道批处理系统

                    20世纪60年代中期,在前述的批处理系统中,引入多道程序设计技术后形成多道批处理系统(简称:批处理系统)。
                    
                    它有两个特点:
                        (1)多道:系统内科同时容纳多个作业,这些作业放在外存中,组成一个后备队列,系统按一定的调度原则每次从后备作业队列中选取一个或多个作业进入内存进行,运行作业结束、退出运行和后备作业进行运行均由系统自动实现,从而在系统中形成一个自动转接的、连续的作业流。
                        (2)成批:在系统运行过程中,不允许用户与其作业发生交互作用,即:作业一旦进入系统,用户就不能直接干预其作业的运行。
                        
                    批处理系统的追求目标:提高系统资源利用率和系统吞吐量,以及作业系统的自动化
                    批处理系统的一个重要缺点:不提供人机交互能力,给用户使用计算机带来不便。
                    虽然用户独占全机资源,并且直接控制程序的运行,可以随时了解程序的运行情况,但这种工作方式因独占全机造成效率极低。
                    一种新的追求目标:既能保证计算机效率,又能方便用户使用计算机,20世纪60年代中期,计算机技术和软件技术的发展使这种追求成为可能。                 

  分时系统               

                    由于CPU速度不断提高和采用分时技术,一台计算机课同时连接多个用户终端,而每个用户可在自己的终端上联机使用计算机,好像自己独占机器一样。
                    
                    分时技术:把处理机的运行时间分成很短的时间片,按时间轮流把处理机分配给各联机作业使用。
                    
                    若某个作业在分配给它的时间片内不能完成其计算,则该作业暂时中断,把处理机让给另一作业使用,等待下一轮时再继续其运行,由于计算机速度很快,作业运行轮转的很快,给每个用户的印象是,好像他独占了一台计算机。
                    而每个用户可以通过自己的终端向系统发出各种操作控制命令,在充分的人机交互情况下,完成作业的运行。
                    
                    具有上述特征的计算机系统称为分时系统,它允许多个用户同时联机使用计算机。
                    
                    特点:
                        (1)多路性。若干个用户同时使用一台计算机,微观上看是用户轮流使用计算机,宏观上看各个用户并行工作。
                        (2)交互性,用户可根据对请求的响应结果,进一步向系统提出新的请求。这种能使用与系统进行人机对话的工作方式,明显地有别与批处理系统,因而,分时系统又称为交互式系统。
                        (3)独立性,用户之间可以户型独立操作,互不干扰,系统保证各用户程序运行的完整性,不会发生互相混淆或破坏现象。
                        (4)及时性,系统可对用户的出入及时作出响应,分时系统性能的主要指标之一是响应时间,它是指:从终端发出命令到系统予以应答所需要的时间。
                        
                    分时系统的主要目标:对用户响应的及时性,即不至于用户等待每一个命令的处理时间过长。

                    分时系统可以同时接纳数十个甚至上百个用户,由于内存空间有限,往往采用对换(又称交换)方式的存储方法。即将未“轮到”的作业放入磁盘,一旦“轮到”,再将其调入内存;而时间片用完后,又将作业存回磁盘(俗称“滚进”、“滚出“法),使同一存储区域轮流为多个用户服务。
                    
                    多用户分时系统是当今计算机操作系统中最普遍使用的一类操作系统。

  实时系统

                    虽然多道批处理系统和分时系统能获得较令人满意的资源利用率和系统响应时间,但却不能满足实时控制与实时信息处理两个应用领域的需求。于是就产生了实时系统,即系统能够及时响应随机发生的外部事件,并在严格的时间范围内完成对该事件的处理。
                    实时系统在一个特定的应用中常作为一种控制设备来使用。
                    
                    实时系统可分成两类:
                        (1)实时控制系统。当用于飞机飞行、导弹发射等的自动控制时,要求计算机能尽快处理测量系统测得的数据,及时地对飞机或导弹进行控制,或将有关信息通过显示终端提供给决策人员。当用于轧钢、石化等工业生产过程控制时,也要求计算机能及时处理由各类传感器送来的数据,然后控制相应的执行机构。
                        (2)实时信息处理系统。当用于预定飞机票、查询有关航班、航线、票价等事宜时,或当用于银行系统、情报检索系统时,都要求计算机能对终端设备发来的服务请求及时予以正确的回答。此类对响应及时性的要求稍弱于第一类。

                    实时操作系统的主要特点:
                        (1)及时响应。每一个信息接收、分析处理和发送的过程必须在严格的时间限制内完成。
                        (2)高可靠性。需采取冗余措施,双机系统前后台工作,也包括必要的保密措施等。

二、进程与线程

1.1 什么是进程?

                程序的执行实例被称为进程。
                程序并不能单独运行,只有将程序装在到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程,程序和进程的区别就在于:程序是指令的集合,它是进程运行的金泰描述文件;进程是程序的一次执行活动,属于动态概念。
                
                在多道编程中,他们允许过个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行,这个这样的设计,大大提高了CPU的利用率,进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的。

  1.1.1 进程的优点

                1、进程提供了多道编程,让我们在宏观上看上去拥有自己的CPU和其他资源,可以提高计算机的利用率。
                2、提高了CPU的利用率。

  1.1.2 进程的缺点

                1、进程只能在一个时间干一件事。
                2、进程在执行的过程中如果堵塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖与输入的数据,也将无法执行。               

1.2 什么是线程

                线程是操作系统能够进行运算调度的最小单位,他被包含在进程中,是进程中的实际运作单位,一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,
                
                线程在执行过程中与进程还是有区别的,每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口,但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
                每个线程都有他自己的一组CPU寄存器,成为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。
                指令指针和堆栈指针寄存器是线程上线文中两个最要的寄存器,线程总是在进程得到上下文中运行,这些地址都用于标志拥有线程的进程地址空间中的内存。

  1.2.1 线程的优点

                1、使用线程可以占据长时间的程序的任务放到后台去处理。
                2、用户界面可以更吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度。
                3、程序的运行速度可以加快。
                4、在一些等待的任务实现上用户输入、文件读写和网络收发数据等,线程就比较有用,在这种情况下我们可以释放一些珍贵的资源如内存占用等。

  1.2.2 线程的缺点

                线程可以被抢占(中断)
                在其他线程正在运行时,线程可以暂时搁置(也称为睡眠)--这就是线程的退让。

1.3 进程与线程的区别

                1、线程共享创建它的进程地址空间;进程有自己的地址空间。(即线程可以共享内存,进程不可以)
                2、线程可以直接访问其进程的数据段,进程有自己的父进程的数据段副本。
                3、线程可以直接与其进程的其他线程通信,进程必须使用进程间通信来与兄弟进程通信。
                4、新线程很容易创建,新进程需要父进程的重复。
                5、线程可以对同意进程的线程执行相当大的控制;进程只能控制子进程。
                6、对主线程的更改可能会影响进程其他线程的行为;对父进程的更改不会影响子进程。

第三章 Python GIL(Global Interpreter Lock)

 

第四章 线程与进程的使用

1.1 Python threading模块

                Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元。
                线程有2种调用方式

  1.1.1 直接调用

#/usr/bin/env python
#-*- coding:utf-8 -*-
import  threading
import time
def show(arg):
    time.sleep(1)
    print('threading',str(arg))
for i in range(10):
    t = threading.Thread(target=show,args=(i,))
    t.start()

print('main thread stop')
结果:
    main thread stop
    threading 1
    threading 0
    threading 3
    threading 5
    threading 9
    threading 8
    threading 7
    threading 4
    threading 2
    threading 6
上述代码创建了10个线程,然后控制器就交给了CPU,CPU根据指定算法进行调度,分片执行指令。
更多:
start       线程准备就绪,等待CPU调度
setName     为线程设置名称
getName     获取线程名称
setDaemon   设置为后台新城或前台线程(默认)
            如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止。
            如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止。
join        逐个执行每个线程,执行完毕后继续往下执行,给方法使得多线程变的无意义
run         线程被CPU调度后自动执行线程对象的run方法。

    自定义线程类               

import threading,time

class MyThread(threading.Thread):
    def __init__(self,num):
        threading.Thread.__init__(self)
        self.num = num

    def run(self):#定义每个线程要运行的函数
        print('running on number:%s'%self.num)
        time.sleep(3)


if __name__ == '__main__':
    t1=MyThread(1)
    t2=MyThread(2)
    t1.start()
    t2.start()
结果:
running on number:1
running on number:2

  1.1.2 join&Daemon

                join是等待每个线程执行完成后再往下进行执行。
                Daemon 将线程修改为主线程的守护线程。
                
                等待线程执行后将线程设为守护线程
                import time,threading

                def run(n):
                    print('[%s]---running---
'%n)
                    time.sleep(2)
                    print('---done----')
                
                def main():
                    for i in range(5):
                        t= threading.Thread(target=run,args=[i,])
                        t.start()
                        t.join(1)
                        print('starting thread',t.getName())
                
                m = threading.Thread(target=main,args=[])
                m.setDaemon(True)#将main线程设置为Daemon线程,他做为程序主线程的守护线程,当主线程退出时,m线程也会退出,由m启动的其他子线程也会同时退出,不管是否执行完任务
                m.start()
                m.join(timeout=2)
                print('---main thread done----')
                结果:
                    [0]---running---

                    starting thread Thread-2
                    [1]---running---
                    
                    ---done----
                    ---main thread done----

  1.1.3 线程锁(Lock、RLock)

                由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,当多个线程同时修改同一条数据时可能会出现脏数据,所以,出现了线程锁-同一时刻允许一个线程执行操作。

      未使用锁

import threading,time

gl_num=0

def show(arg):
    global gl_num
    time.sleep(2)
    gl_num +=1
    print(gl_num)

for i in range(10):
    t=threading.Thread(target=show,args=(i,))
    t.start()

print('main thread stop')
结果:
main thread stop
1
2
3
4
5
6
7
8
9
10
注意:现在python3中已经避免会写错的问题了,如果是python2的话,就会少些一次,这是为什么呢?
是因为多个线程同时去写同一份数据时,例如:线程A获取num的值为0,交给CPU去写时,在写的过程中,具有I/O的操作,多道的特性是在去I/O活动是,要进行下一个线程执行,因此,当A去写的时候,这个线程为挂起状态,这个时候线程B获取num的值为0继续去写,因此就会少值,因此程序就不稳定。

      使用递归线程锁

                   

import threading,time

cx_num = 0

lock = threading.RLock()

def Func():
    lock.acquire()
    global cx_num
    cx_num +=1
    # time.sleep(1)
    print(cx_num)
    lock.release()

for i in range(10):
    t = threading.Thread(target=Func)
    t.start()

print('main threading stop')
结果:
1
2
3
4
5
6
7
8
9
10
main threading stop

  1.1.4 信号量

                互斥锁,同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据,比如侧说有3个坑,那么最多只允许3个人上厕所,后面的只能等里面有人踹才能在进去。
                        
                  

import  threading,time

def run(n):
    semaphore.acquire()
    time.sleep(1)
    print('run the thread :%s'%n)
    semaphore.release()


if __name__ == '__main__':
    num = 0
    semaphore = threading.BoundedSemaphore(5) #最多允许5个线程同时运行
    for i in range(20):
        t= threading.Thread(target=run,args=(i,))
        t.start()
  作用:     控制根据机器的配置,开控制同时并发开放几个线程。                           

  1.1.5 事件(event)

                Python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法set、wait、clear。
                事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为False,那么当程序执行event.wait方法时会阻塞,如果“Flag”值为True,那么event.wait方法时便不再阻塞。
                 clear:将“flag”设置为False
                 set:将“flag”设置为True

      通过Event来实现两个或多个线程间的交互,下面是一个红绿灯的例子,即启动一个线程做交通指挥灯,即启动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红灯停,绿灯行的规则。

import threading,time,random

def light():
    if not event.isSet():
        event.set()
    count =0
    while True:
        if count <10:
            print('33[42;1m---green light on ---33[0m')
        elif count <13:
            print('33[43;1m---yellow light on ---33[0m')
        elif count <20:
            if event.isSet():
                event.clear()
            print('33[41;1m --red light on -- 33[0m')
        else:
            count=0
            event.set()
        time.sleep(1)
        count +=1


def car(n):
    while 1:
        time.sleep(random.randrange(10))
        if event.is_set():
            print('car [%s] is running..'%n)
        else:
            print('car [%s] is waiting for the red light..'%n)

if __name__ == '__main__':
    event= threading.Event()
    Light = threading.Thread(target=light)
    Light.start()
    for i in range(3):
        t = threading.Thread(target=car,args=(i,))
        t.start()

      这里还有个event使用的例子,员工进公司门要刷卡,我们这里设置一个线程是“门”,再设置几个线程为“员工”,员工看到门没开,就刷卡,刷完卡,门开了,员工就可以通过。
#-*-config:utf-8-*-
import threading,time,random
def door():
    door_open_time_counter=0
    while True:
        if door_swiping_event.is_set():
            print('33[32;1m door opening...33[0m')
            door_open_time_counter +=1
        else:
            print('33[31;1m door closed....,swipe to open.33[0m')
            door_open_time_counter=0
            door_swiping_event.wait()
        if door_open_time_counter >3:
            door_swiping_event.clear()
        time.sleep(0.5)

def staff(n):
    print('staff [%s] is comming...'%n)
    while 1:
        if door_swiping_event.is_set():
            print('33[34;1m door is opened ,passing....33[0m ')
            break
        else:
            print('staff [%s] sees door got closed,swipping the card....'%n)
            print(door_swiping_event.set())
            door_swiping_event.set()
            print('after set',door_swiping_event.set())
        time.sleep(0.5)

door_swiping_event = threading.Event()

door_thread = threading.Thread(target=door)

door_thread.start()

for i in range(5):
    p = threading.Thread(target=staff,args=(i,))
    time.sleep(random.randrange(3))
    p.start()

  1.1.6 条件(Condition)

                使得线程等待,只有满足某条件时,才释放n个线程。

import  threading
def run(n):
    con.acquire()
    con.wait()
    print('run the thread :%s'%n)
    con.release()

if __name__ == '__main__':
    con = threading.Condition()
    for i in range(10):
        t = threading.Thread(target=run,args=(i,))
        t.start()

    while True:
        inp = input('>>>>')
        if inp == 'q':
            break
        con.acquire()
        con.notify(int(inp))
        con.release()

  1.1.7 Timer定时器

                定时器,指定n秒后执行某操作。 

from threading import  Timer

def hello():
    print("hello,world")

t = Timer(10,hello)
t.start()

  1.1.8 queue队列

                队列分为三种,第一种为队列(先进先出),第二种为堆栈(先进后出),第三种为优先级
                    #import queue
                    # q = queue.Queue(maxsize=2)  #设置队列2个为满了,先进先出
                    # q1 = queue.LifoQueue()#先进后出
                    # q.put(1)
                    # q.get()
                    # q.empty()#判断队列是不是为空,返回True和False
                    # q.full()#判断队列是否满了,返回True和False
                    # q.get_nowait()#如果没有值,不会等待,直接报错
                    # q.put_nowait()#如果队列满了,不会等待,直接报错。
                    # q.qsize()#打印当前大小
                    # #当多个线程同时put一个队列时,不需要加锁,因为这个队列是线程安全的,会自己加锁。
                    # q2=queue.PriorityQueue()#优先级 越小越优先。
                    # q2.put([3,'cx']) #
                    # q2.put([1,'cc'])
                    # print(q2.get())
                    # print(q2.get())                                    

  生产者消费者模型               

                 在并发编程中使用生产者和消费者模式,能够解决绝大多数并发问题,该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。

    为什么要使用生产者和消费者模型

                    在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程,在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据,同样的道理,如果消费者处理能力大于生产者,那么消费者必须等待生产者,为了解决这个问题,于是引入了生产者和消费者的模式。

    什么是生产者消费者模式

                    生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题,生产者和消费彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产这和消费者的处理能力。

      简单生产者消费者模型:                    

import threading,queue
def producer():
    for i in range(10):
        q.put('骨头 %s'%i)
    print('开始等待所有的骨头被吃掉。。。。')
    q.join()
    print('所有的骨头被吃完...')


def consumer(n):
    while q.qsize() >0:
        print('%s 正在吃'%n,q.get())
        q.task_done() #告知这个任务执行完了

q=queue.Queue()

p=threading.Thread(target=producer)
p.start()

c1=consumer("司家勇")

      多线程实现生产者消费者模型

import time,random,queue,threading
q=queue.Queue()

def producer(name):
    count=0
    while count < 20:
        time.sleep(random.randrange(3))
        q.put(count)
        print('producer %s has produced %s 翔...'%(name,count))
        count +=1

def consumer(name):
    count=0
    while count <20:
        time.sleep(random.randrange(4))
        if not q.empty():
            data=q.get()
            print(data)
            print('33[32;1m Consumer %s has eat %s 翔....33[0m'%(name,data))
        else:
            print('-----no baozi anymore-----')
        count +=1

p1=threading.Thread(target=producer,args=('陈鑫',))
c1=threading.Thread(target=consumer,args=('白弘毅',))
p1.start()
c1.start()

1.2 多进程           

  1.2.1 multiprocessing模块

                直接从侧面用subprocesses替换线程使用GIL的方式,由于这一点,multiprocessing模块可以让程序员在给定的机器上充分的利用CPu,在multiprocessing中,通过创建Process对象生成进程,然后调用他的start()方法。
                

from multiprocessing import Process
import time

def f(name):
    time.sleep(1)
    print('33[31;1m hello %s33[0m'%name)

if __name__ == '__main__':
    p=Process(target=f,args=('陈鑫',))
    p.start()
    p.join() #等待进程执行完毕
     要显示涉及的单个进程ID,下面是一个扩展示例:
from multiprocessing import Process
import os

def info(title):
    print(title)
    print('module name:',__name__)
    print('parent process:',os.getppid())
    print('process id:',os.getpid())
    print('

')

def f(name):
    info('33[31;1m main Process line 33[0m')
    print('hello',name)
if __name__ == '__main__':
    p=Process(target=f,args=('bob',))
    p.start()
    p.join()

  1.2.2 进程间通讯

                    不同进程内存是不共享的,要先实现两个进程的数据交换,可以用以下方法:            

    Queues

  使用方法跟threading里差不多。
from multiprocessing import Process,Queue

def f(q):
    q.put([42,None,'hello'])

if __name__ == '__main__':
    q=Queue()
    p=Process(target=f,args=(q,))
    p.start()
    print(q.get())
    p.join()

    Pipes

from multiprocessing import Process,Pipe

def f(conn):
    conn.send([42,None,'hello'])
    conn.close()


if __name__ == '__main__':
    parent_conn,child_conn = Pipe()
    p=Process(target=f,args=(child_conn,))
    p.start()
    print(parent_conn.recv())
    p.join()

    在使用并发设计的时候最好尽可能的避免共享数据,尤其是在使用多进程的时候,如果你真的需要共享数据,multiprocessing提供了两种方式。        

    1、multiprocessing,Array,Value

                        数据可以用Value和Array存在一个共享内存地址中,如下:          

from multiprocessing import Array,Value,Process
def func(a,b):
    a.value = 3.333333333333333
    for i in range(len(b)):
        b[i]=-b[i]

if __name__ == '__main__':
    num=Value('d',0.0)
    arr=Array('i',range(11))

    c=Process(target=func,args=(num,arr))
    d=Process(target=func,args=(num,arr))
    c.start()
    d.start()
    c.join()
    d.join()

    print(num.value)
    for i in arr:
        print(i)

    2、multiprocessing Manager 实现内存共享

                   由Manager()返回manager提供list,dict,Namespace,Lock,RLock,Semaphore,BoundedSemaphore,Condition,Event,Barrier,Queue,Valueand Array类型的支持。                      

from multiprocessing import Process,Manager

def f(d,l):
    d[1]='1'
    d['2']=2
    d[0.25]=None
    l.append(1)
    print(l)

if __name__ == '__main__':
    with Manager() as manager:
        d=manager.dict()

        l=manager.list(range(5))
        p_list=[]

        for i in range(10):
            p=Process(target=f,args=(d,l))
            p.start()
            p_list.append(p)

        for res in p_list:
            res.join()

        print(d)
        print(l)

    进程同步

from multiprocessing import Process,Lock
def f(l,i):
    l.acquire()
    try:
        print('hello world',i)
    finally:
        l.release()

if __name__ == '__main__':
    lock=Lock()

    for num in range(10):
        Process(target=f,args=(lock,num)).start()

  1.2.3 进程池

                    进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供应使用的进程,那么程序就会等待,直到进程池中又没有可用进程为止。
                    进程池中又两个方法:
                    apply
                    apply_async
                    

from multiprocessing import Process,Pool
import  time

def Foo(i):
    time.sleep(1)
    return i+100

def Bar(arg):
    print('--->exec done',arg)

pool=Pool(5)

for i in range(100):
    pool.apply_async(func=Foo,args=(i,),callback=Bar)

print('end')
pool.close()

pool.join() #进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭
原文地址:https://www.cnblogs.com/cxcx/p/6189707.html