Cpython解释器支持的进程与线程

Cpython解释器支持的进程与线程

一 python并发编程之多进程

1.1 multiprocessing模块介绍

    python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程。Python提供了非常好用的多进程包multiprocessing。
    multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。

  multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。

    需要再次强调的一点是:与线程不同,进程没有任何共享状态,进程修改的数据,改动仅限于该进程内。

1.2 Process类的介绍

    创建进程的类

Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)

强调:
1. 需要使用关键字的方式来指定参数
2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号

参数介绍:

goroup参数未被使用,他的值始终是None
targe表示调用对象,简单来说就是子进程要执行的任务
args表示调用对象的位置参数元祖,args=(1,2,'egon',)
kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
name 是子进程的名称

 属性介绍:

1 p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
2 
3 p.name:进程的名称
4 
5 p.pid:进程的pid
6 
7 p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
8 
9 p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)

1.3 Process类的使用

=====================part1:创建并开启子进程的两种方式

注意:在windows中Process()必须放到# if __name__ == '__main__':下

Since Windows has no fork, the multiprocessing module starts a new Python process and imports the calling module. 
If Process() gets called upon import, then this sets off an infinite succession of new processes (or until your machine runs out of resources). 
This is the reason for hiding calls to Process() inside

if __name__ == "__main__"
since statements inside this if-statement will not get called upon import.

由于Windows没有fork,多处理模块启动一个新的Python进程并导入调用模块。 
如果在导入时调用Process(),那么这将启动无限继承的新进程(或直到机器耗尽资源)。 
这是隐藏对Process()内部调用的原,使用if __name__ == “__main __”,这个if语句中的语句将不会在导入时被调用。

  Process对象的其他方法或属性

from multiprocessing import Process
import os
import time
def work():
    print('%s is working'%os.getpid())
    time.sleep(3)
if __name__=='__main__':
    p1=Process(target=work)
    # p2 = Process(target=work)
    # p3 = Process(target=work)
    # p4 = Process(target=work)
    # p1.daemon=True
    # p2.daemon = True
    # p3.daemon = True
    # p4.daemon = True
    p1.start()
    p1.terminate()
    time.sleep(2)
    print(p1.is_alive())
    # p2.start()
    # p3.start()
    # p4.start()
    # p1.join()
    # # p2.join()
    # # p3.join()
    # # p4.join()
    print('基于初始化结果运行')

这里我们要注意了,这里的.join()是主进程在等子进程的结束,是主进程阻塞在原地,而子进程还是在后台执行的

接下来我们讲一个,叫,进程同步锁

进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或者同一个打印终端,是没有问题的,

例子1,共享同一打印终端,发现会有多行内容打印到一行的现象(多个进程共享并抢占同一个打印终端,这时候就乱了)

共享同一个文件,有的同学会想到,既然可以用文件共享数据,那么进程间通信用文件作为数据传输介质就可以了啊,可以,但是有问题,1.效率   2.需要自己加锁处理

在这里,我们一定要知道的是,加锁的目的是为了保证多个进程修改同一快数据是,同一时间只能有一个在修改,简单来说就是串行的修改,在这里速度是慢了,但是保证了数据的安全

在这里我们先模拟一个抢票(Lock互斥锁)

记住文件是字典的形式,{"count":1}还有这里一定要用双引号,不然json是不识别的
from
multiprocessing import Process,Lock import json import time import random import os def search(): dic=json.load(open('db.txt',)) print('剩余票数%s'%dic['count']) def get_ticket(): dic=json.load(open('db.txt',)) if dic['count']>0: dic['count']-=1 time.sleep(random.randint(1,3)) json.dump(dic,open('db.txt','w')) print('%s 购票成功'%os.getpid()) def task(mutex): search() mutex.acquire() get_ticket() mutex.release() if __name__=='__main__': mutex=Lock() for i in range(20): p=Process(target=task,args=(mutex,)) p.start()

接下来我们讲一下队列的问题

  进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的

    创建队列的类(底层就是以管道和锁定的方式实现)

1 Queue([maxsize]):创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。

 方法介绍:

    主要方法:
复制代码
1 q.put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。
2 q.get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常.
3  
4 q.get_nowait():同q.get(False)
5 q.put_nowait():同q.put(False)
6 
7 q.empty():调用此方法时q为空则返回True,该结果不可靠,比如在返回True的过程中,如果队列中又加入了项目。
8 q.full():调用此方法时q已满则返回True,该结果不可靠,比如在返回True的过程中,如果队列中的项目被取走。
9 q.qsize():返回队列中目前项目的正确数量,结果也不可靠,理由同q.empty()和q.full()一样
复制代码
    其他方法(了解):
1 q.cancel_join_thread():不会在进程退出时自动连接后台线程。可以防止join_thread()方法阻塞
2 q.close():关闭队列,防止队列中加入更多数据。调用此方法,后台线程将继续写入那些已经入队列但尚未写入的数据,但将在此方法完成时马上关闭。如果q被垃圾收集,将调用此方法。关闭队列不会在队列使用者中产生任何类型的数据结束信号或异常。例如,如果某个使用者正在被阻塞在get()操作上,关闭生产者中的队列不会导致get()方法返回错误。
3 q.join_thread():连接队列的后台线程。此方法用于在调用q.close()方法之后,等待所有队列项被消耗。默认情况下,此方法由不是q的原始创建者的所有进程调用。调用q.cancel_join_thread方法可以禁止这种行为
from multiprocessing import Process,Queue
q=Queue(3)
q.put('first',block=False,timeout=10)
q.put('second')
q.put('third')
q.put('third',timeout=10)
print(q.get())
print(q.get())
print(q.get())



原文地址:https://www.cnblogs.com/liuchengdong/p/7424816.html