进程同步( 使用Queue&JoinableQueue)
#coding=utf-8
import multiprocessing
import time
class Consumer(multiprocessing.Process):
def __init__(self,task_queue,result_queue):
multiprocessing.Process.__init__(self)
self.task_queue = task_queue
self.result_queue = result_queue
def run(self):
proc_name = self.name
while True:
next_task =self.task_queue.get()
if next_task is None:
print("%s Exiting" %proc_name)
self.task_queue.task_done()
break
print("%s:%s" %(proc_name,next_task))
answer = next_task()
self.task_queue.task_done()
self.result_queue.put(answer)
return
class Task(object):
def __init__(self,a,b):
self.a = a
self.b = b
def __call__(self):
time.sleep(0.1)
return "%s * %s = %s" %(self.a,self.b,self.a * self.b)
def __str__(self):
return "%s * %s " %(self.a,self.b)
if __name__ == "__main__":
tasks = multiprocessing.JoinableQueue()
results = multiprocessing.Queue()
num_consumers = multiprocessing.cpu_count()
print("Creating %d consumer" %num_consumers)
consumers = [Consumer(tasks,results) for i in range(num_consumers)]
for w in consumers:
w.start()
num_jobs = 10
for i in range(num_jobs):
tasks.put(Task(i,i))
for i in range(num_consumers):
tasks.put(None)
tasks.join()
while num_jobs:
result = results.get()
print("Result: %s" %result)
num_jobs -= 1
进程同步(加锁-Lock)
不加锁
加锁
锁是为了确保数据一致性,比如读写锁,每个进程给一个变量增加 1 ,但是如果在一个进程读取但还没有写入的时候,另外的进程也同时读取了,并写入该值,则最后写入的值是错误的,这时候就需要加锁来保持数据一致性
进程同步(加锁-Semaphore)
Semaphore用于控制对共享资源的访问数量。Semaphore锁和Lock稍有不同,Semaphore相当于N把锁,获取其中一把就可以执行。可用锁的总数N在创建实例时传入,比如s = Semaphore(n)。与Lock一样,如果可用锁为0,进程将会阻塞,直到可用锁大于0。
#coding=utf-8
import multiprocessing
import time
def worker(s,i):
s.acquire()
print(multiprocessing.current_process().name + "acquire")
time.sleep(i)
print(multiprocessing.current_process().name + "release")
s.release()
if __name__== "__main__":
s = multiprocessing.Semaphore(3)
for i in range(5):
p = multiprocessing.Process(target=worker,args=(s,i*2))
p.start()
进程同步(信号传递-Event)
Event提供一种简单的方法,可以在进程间传递状态信息,实现进程间同步通信。事件可以切换设置和未设置状态。通过使用一个可选的超时值,时间对象的用户可以等待其状态从未设置变为设置。
#coding=utf-8
import multiprocessing
import time
def wait_for_event(e):
print("wait_for_event: starting")
e.wait() #等待收到能执行信号,如果一直未收到将一直阻塞,死等
print("wait_for_event: e.is_set()->",e.is_set())
print("可以继续做一些事情了。")
def wait_for_event_timeout(e,t):
print("wait_for_event_timeout: starting")
e.wait(t)#等待t秒超时,此时Event的状态仍未未设置 print("wait_for_event_timeout: e.is_set()->",e.is_set())
e.set()#设置Event的状态
print("信号状态已被设置")
if __name__ == "__main__":
e = multiprocessing.Event()
print("begin,e.is_set()",e.is_set())
p1 = multiprocessing.Process(name = "block",target=wait_for_event,args=(e,))
p1.start()
p2 = multiprocessing.Process(name = "nonblock",target=wait_for_event_timeout,args=(e,2))
p2.start()
time.sleep(3)
print("main: event is set")
进程同步(使用管道-Pipe)
Pipe是两个进程间通信的工具。Pipe可以是单向(half-duplex),也可以是双向(duplex)。我们通过mutiprocessing.Pipe(duplex=False)创建单向管道 (默认为双向)。一个进程从PIPE一端输入对象,然后被PIPE另一端的进程接收,单向管道只允许管道一端的进程输入,而双向管道则允许从两端输入。Pipe的每个端口同时最多一个进程读写,否则会出现各种问题,可能造成corruption异常。Pipe对象建立的时候,返回一个含有两个元素的元组对象,每个元素代表Pipe的一端(Connection对象)。我们对Pipe的某一端调用send()方法来传送对象,在另一端使用recv()来接收。
#coding=utf-8
import multiprocessing as mp
def proc_1(pipe):
pipe.send("hello")
print("proc_1 received: %s" %pipe.recv())
pipe.send("what is your name? ")
print("proc_1 received: %s " %pipe.recv())
def proc_2(pipe):
print("proc_2 received : %s" %pipe.recv())
pipe.send("hello ,too")
print("proc_2 received: %s" %pipe.recv())
pipe.send("I don't tell you!")
if __name__ == "__main__":
pipe = mp.Pipe()
print(len(pipe))
print(type(pipe))
p1 = mp.Process(target=proc_1,args=(pipe[0],))
p2 = mp.Process(target=proc_2,args=(pipe[1],))
p2.start()
p1.start()
p1.join()
p2.join()
#coding=utf-8
import multiprocessing as mp
from multiprocessing import Process,Lock
def write_file(content,lock,file_path="e:\\a.txt"):
lock.acquire() #保证同时只有一个进程在写文件
with open(file_path,"a") as fp:
fp.write(content + "\n")
lock.release()
def proc_1(pipe,lock):
pipe.send("hello")
info = pipe.recv()
print("proc_1 recieved: %s" %info)
write_file(info,lock)
pipe.send("what is your name?")
info = pipe.recv()
print("proc_1 recieved: %s" %info)
write_file(info,lock)
def proc_2(pipe,lock):
info = pipe.recv()
print("proc_2 recieved: %s " %info)
write_file(info,lock)
pipe.send("hello too")
info = pipe.recv()
print("proc_2 recieved: %s" %info)
write_file(info,lock)
pipe.send("I don't tell you!")
if __name__ == "__main__":
pipe = mp.Pipe()#创建一个管道对象,为一个元组,包含了pipe的两端
lock = Lock()#创建一个锁对象
print(len(pipe))
print(type(pipe))
#创建两个子进程
p1 = mp.Process(target=proc_1,args=(pipe[0],lock))
p2 = mp.Process(target=proc_2,args=(pipe[1],lock))
p1.start()
p2.start()
p1.join()
p2.join()
#coding=utf-8
import multiprocessing as mp
from multiprocessing import Process,Lock
def write_file(content,lock,file_path="e:\\a.txt"):
lock.acquire()
with open(file_path,"a") as fp:
fp.write(content + "\n")
lock.release()
def proc_1(pipe,lock):
pipe.send("hello")
info = pipe.recv()
print("proc_1 recieved: %s" %info)
write_file(info,lock)
pipe.send("what is your name?")
info = pipe.recv()
print("proc_1 recieved: %s" %info)
write_file(info,lock)
def proc_2(pipe,lock):
info = pipe.recv()
print("proc_2 recieved: %s " %info)
write_file(info,lock)
pipe.send("hello too")
info = pipe.recv()
print("proc_2 recieved: %s" %info)
write_file(info,lock)
pipe.send("I don't tell you!")
if __name__ == "__main__":
p1_list = []
p2_list = []
pipe = mp.Pipe()
lock = Lock()
print(len(pipe))
print(type(pipe))
for i in range(5):
p1_list.append(mp.Process(target=proc_1,args=(pipe[0],lock)))
for i in range(5):
p2_list.append(mp.Process(target=proc_2,args=(pipe[1],lock)))
for i in range(5):
p1_list[i].start()
p2_list[i].start()
for i in range(5):
p1_list[i].join()
p2_list[i].join()
print("***********")
进程同步(使用Condition)
一个condition变量总是与某些类型的锁相联系,当几个condition变量必须共享同一个锁的时候,是很有用的。锁是conditon对象的一部分:没有必要分别跟踪。condition变量服从上下文管理协议:with语句块封闭之前可以获取与锁的联系。 acquire() 和release() 会调用与锁相关联的相应方法。
wait()方法会释放锁,当另外一个进程使用notify() or notify_all()唤醒它之前会一直阻塞。一旦被唤醒,wait()会重新获得锁并返回,Condition类实现了一个conditon变量。这个condition变量允许一个或多个进程等待,直到他们被另一个进程通知。如果lock参数,被给定一个非空的值,那么他必须是一个lock或者Rlock对象,它用来做底层锁。否则,会创建一个新的Rlock对象,用来做底层锁。
wait(timeout=None) :等待通知,或者等到设定的超时时间。当调用这wait()方法时,如果调用它的进程没有得到锁,那么会抛出一个RuntimeError异常。 wait()释放锁以后,在被调用相同条件的另一个进程用notify() or notify_all() 叫醒之前会一直阻塞如果有等待的进程,notify()方法会唤醒一个在等待conditon变量的进程。
notify_all() 则会唤醒所有在等待conditon变量的进程。
注意:
notify()和notify_all()不会释放锁,也就是说,进程被唤醒后不会立刻返回他们的wait() 调用。除非进程调用notify()和notify_all()之后放弃了锁的所有权。
在典型的设计风格里,利用condition变量加锁去允许访问一些共享状态,进程在获取到它想得到的状态前,会反复调用wait()。修改状态的进程在他们状态改变时
调用 notify() or notify_all(),用这种方式,进程会尽可能的获取到想要的一个等待者状态
#coding=utf-8
import multiprocessing as mp
import time
def consumer(cond):
with cond:#此处获得与锁的联系
print("consumer before wait")
cond.wait() #释放锁
#程序在此一直等待,等待notify()或notify_all()唤醒
print("consumer after wait")
def producer(cond):
with cond:
print("producer before notifyAll")
cond.notify_all()#唤醒所有等待锁的进程,此处为通知消费者可以消费了
print("producer after notifyAll")
if __name__ == "__main__":
condition = mp.Condition()
p1 = mp.Process(name = "p1",target=consumer,args=(condition,))
p2 = mp.Process(name = "p2",target=consumer,args=(condition,))
p3 = mp.Process(name = "p3",target = producer,args = (condition,))
p1.start()
time.sleep(2)
p2.start()
time.sleep(2)
p3.start()
#注意消费者进程的启动需要放在生产者启动之前,否则会出现生产者已经notify的情况下,消费者会无限等待的情况
注意消费者进程的启动需要放在生产者启动之前,否则会出现生产者已经notify的情况下,消费者会无限等待的情况
示例:
#coding=utf-8
import multiprocessing as mp
import time
def consumer(cond):
with cond:
print("consumer before wait")
cond.wait()
print("consumer after wait")
def producer(cond):
with cond:
print("producer before notifyAll")
cond.notify_all()
print("producer after notifyAll")
if __name__ == "__main__":
condition = mp.Condition()
p1 = mp.Process(name = "p1",target=consumer,args=(condition,))
p2 = mp.Process(name = "p2",target=consumer,args=(condition,))
p3 = mp.Process(name = "p3",target = producer,args = (condition,))
p1.start()
time.sleep(2)
p3.start()
#p3#此处生产者进程放在了消费者进程p2的前面,注意查看输出
time.sleep(2)
p2.start()
多进程间共享数字变量
未使用共享变量
#coding=utf-8
from multiprocessing import Process
def f(n,a):
n = 3.1415
for i in range(len(a)):
a[i] = -a[i]
if __name__ == "__main__":
num = 0 #主进程的num
arr = list(range(10))#主进程的arr
p = Process(target = f,args=(num,arr))#修改了传入f函数的值,但是数据属于子进程,主进程不能访问
p.start()
p.join()
print(num)
print(arr[:])
结果为:主进程变量的值
注意:
此程序存在两个进程 主进程和创建的子进程p,两个进程的数据是相互独立的,if __name__ == '__main__':模块下的num,arr属于主进程,这两个参数被传入子进程执行的函数后,存在于子进程的空间内,和主进程不影响;
使用共享变量
在上例中,主进程、子进程间要共享变量的话,需要创建能在进程间共享的变量;
#coding=utf-8
from multiprocessing import Process,Value,Array
def f(n,a):
n.value = 3.1415
for i in range(len(a)):
a[i] = -a[i]
if __name__ == "__main__":
num = Value("d",0.0)#创建一个进程间共享的数字类型,默认值为0
arr = Array("i",range(10))#创建一个进程间共享的数组类型,初始值为range[10]
p = Process(target=f,args=(num,arr))
p.start()
p.join()
print(num.value)#打印共享变量num的值
print(arr[:])
共享变量、加锁
#coding=utf-8
from multiprocessing import Process,Value,Lock
import time
class Counter(object):
def __init__(self,initval = 0):
self.val = Value("i",initval)
self.lock = Lock()
def increment(self):
with self.lock:#加锁,保证同一时刻只能有一个进程在操作共享变量
self.val.value += 1
def value(self):
with self.lock:
return self.val.value
def func(counter):
for i in range(50):
time.sleep(0.01)
counter.increment()
if __name__ == "__main__":
counter = Counter(0)
procs = [Process(target=func,args=(counter,)) for i in range(10)]
for p in procs:
p.start()
for p in procs:
p.join()
print(counter.value())
多进程间共享字符串变量
#coding=utf-8
from multiprocessing import Process,Value,Manager
from ctypes import c_char_p
def greet(shareStr):
shareStr.value = shareStr.value + ",World!"
if __name__ == "__main__":
manager = Manager()
shareStr = manager.Value(c_char_p,"Hello")#主进程的变量
process = Process(target = greet,args=(shareStr,))#子进程共享主进程的字符串变量
process.start()
process.join()
print(shareStr.value)
多进程间共享不同类型的数据结构对象
#coding=utf-8
from multiprocessing import Process,Manager
def f(shareDict,shareList):
shareDict[1] = "1"
shareDict["2"] = 2
shareDict[0.25] = None
shareList.reverse()
if __name__ == "__main__":
manager = Manager()
shareDict = manager.dict() #创建共享的字典
shareList = manager.list(range(10)) #创建共享的列表
p = Process(target=f,args=(shareDict,shareList))
p.start()
p.join()
print(shareDict)
print(shareList)
Manager()函数返回一个管理对象,它控制了一个服务端进程,用来保持Python对象,并允许其它进程使用代理来管理这些对象。Manager()返回的管理者,支持类型包括,list, dict, Namespace, Lock,RLock, Semaphore, BoundedSemaphore,Condition, Event, Queue, Value and Array。managers比使用共享内存对象更灵活,因为它支持任意对象类型。
同样的,单个的manager可以通过网络在不同机器上进程间共享。但是,会比共享内存慢。
进程间共享实例对象
#coding=utf-8
from multiprocessing import Pool,Value,Lock
from multiprocessing.managers import BaseManager
import os,time
class MyManager(BaseManager):
pass
def Manager():
m = MyManager()
m.start()
return m
class Counter(object):
def __init__(self,initval=0):
self.val = Value("i",initval)
self.lock = Lock()
def increment(self):
with self.lock:
self.val.value += 1
def value(self):
with self.lock:
return self.val.value
MyManager.register("Counter",Counter)
def long_time_task(name,counter):
time.sleep(0.2)
print("Run task %s (%s)...\n" %(name,os.getpid()))
start = time.time()
for i in range(50):
time.sleep(0.01)
counter.increment()
end = time.time()
print("Task %s runs %0.2f seconds." %(name,(end-start)))
if __name__ == "__main__":
manager = Manager()
counter = manager.Counter(0)
print("Parent process %s." %os.getpid())
p = Pool()
for i in range(5):
p.apply_async(long_time_task,args=(str(i),counter))
print("Waiting for all subprocesses done...")
p.close()
p.join()
print("All subprocesses done.")
print(counter.value())
进程日志
#encoding=utf-8
import multiprocessing
import logging
import sys
def worker():
print("I am working...")
sys.stdout.flush()
if __name__ == "__main__":
#设置日志输出到控制台
multiprocessing.log_to_stderr()
#获取日志对象
logger = multiprocessing.get_logger()
#设置日志的级别
logger.setLevel(logging.INFO)
p = multiprocessing.Process(target=worker)
p.start()
p.join()
守护进程
守护进程就是不阻挡主进程退出,会随着主进程退出而退出,如果要等待守护进程退出,需要加上join函数。
#encoding=utf-8
import multiprocessing
import logging
import sys,time
def daemon():
p = multiprocessing.current_process()
print("Starting: ",p.name,p.pid)
#缓冲区数据显示到终端
sys.stdout.flush()
print("Exiting: ",p.name,p.pid)
sys.stdout.flush()
def non_daemon():
p = multiprocessing.current_process()
print("Starting:",p.name,p.pid)
sys.stdout.flush()
print("Exiting: ",p.name,p.pid)
sys.stdout.flush()
if __name__ == "__main__":
#设置日志输出到控制台
multiprocessing.log_to_stderr()
#获取日志对象
logger = multiprocessing.get_logger()
#设置日志级别
logger.setLevel(logging.DEBUG)
d = multiprocessing.Process(name="daemon",target=daemon)
#设置进程d为守护进程
d.daemon = True
n = multiprocessing.Process(name="nondaemon",target=daemon)
#设置进程n为非守护进程
n.daemon = False
d.start()
#time.sleep(1)#不要sleep
n.start()
#d.join()
#n.join()
print("d.is_alive()",d.is_alive())
print("n.is_alive()",n.is_alive())
print("main Process end! ")
主进程首先执行完成,守护进程随着主进程退出直接退出了,根据没有执行守护进程的内容
import subprocess
subprocess.run(args=['ls','-al'])
>>> subprocess.run(["dir","test"],shell = True,cwd="e:\\")
>>> subprocess.Popen(["dir","test"],shell = True,cwd="e:\\")
<subprocess.Popen object at 0x00000000025DA7F0>
subprocess.run(["ls","-al"],shell=True,cwd="/home")
subprocess模块
从Python2.4版本以后,可以使用subprocess这个模块来产生子进程,并连接到子进程的标准输入/输出/错误中去,还可以得到子进程的返回值。看一下官方的解释:
DESCRIPTION
This module allows you to spawn processes, connect to their
input/output/error pipes, and obtain their return codes.
即允许你去创建一个新的进程让其执行另外的程序,并与它进行通信,获取标准的输入、标准输出、标准错误以及返回码等。另外subprocess还提供了一些管理标准流(standard stream)和管道(pipe)的工具,从而在进程间使用文本通信。
创建subprocess进程
subprocess模块用subprocess.Popen类来创建进程,并与进程进行复杂的交互。其构造函
数如下:
__init__(self, args, bufsize=0, executable=None, stdin=None, stdout=None,
stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None,
universal_newlines=False, startupinfo=None, creationflags=0)
主要参数说明:
✓ args:
args should be a string, or a sequence of program arguments.也就是说必须是一个字符
串或者序列类型(如:字符串、list、元组),用于指定进程的可执行文件及其参数。如果是一个序列类型参数,则序列的第一个元素通常都必须是一个可执行文件的路径。当然也可以使用executeable参数来指定可执行文件的路径。
✓ bufsize:
如果指定了bufsize参数作用就和内建函数open()一样:0表示不缓冲,1表示行缓冲,其他正
数表示近似的缓冲区字节数,负数表示使用系统默认值。默认是0。
✓ stdin,stdout,stderr:
分别表示程序的标准输入、标准输出、标准错误。有效的值可以是PIPE,存在的文件描述符
,存在的文件对象或None,如果为None需从父进程继承过来;stdout可以是PIPE,表示对
子进程创建一个管道;stderr可以是STDOUT,表示标准错误数据应该从应用程序中捕获并
作为标准输出流stdout的文件句柄。
✓ shell:
如果这个参数被设置为True,程序将通过shell来执行。
✓ env:
它描述的是子进程的环境变量。如果为None,子进程的环境变量将从父进程继承而来。
创建Popen类的实例对象:
res = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
cmd:向子进程传入需要执行的shell命令,如:ls –al
subprocess.PIPE:在创建Popen对象时,subprocess.PIPE可以初始化为stdin, stdout或stderr的参数,表示与子进程通信的标准输入流,标准输出流以及标准错误,将多个子进程的输入和输出连接在一起,构成管道(pipe)。
subprocess.STDOUT:作为Popen对象的stderr的参数,表示将标准错误通过标准输出流输出。
示例1:
在目录e:\\test创建一个文件夹hhq2
>>> a = subprocess.Popen("mkdir hhq2",shell=True,cwd="e:\\test")#命令为一个字符串
>>> a
<subprocess.Popen object at 0x000000000280A080>
>>> a = subprocess.Popen(["mkdir", "hhq4"],shell=True,cwd="e:\\test")#命令为一个里列表
>>> a
<subprocess.Popen object at 0x000000000255BFD0>
>>> print(a)
<subprocess.Popen object at 0x000000000255BFD0>
示例2:
#encoding=utf-8
import subprocess
#得到子进程obj,执行python命令,标准输入、输出都是管道
obj = subprocess.Popen
(["python"],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
#从标准输入写入内容
obj.stdin.write(b"print(1);")#需要写入bytes类型,
obj.stdin.write(b"print(2);")
obj.stdin.write(b"print(3);")
obj.stdin.write(b"print(4);")
obj.stdin.close()
#读出标准输出的内容
cmd_out = obj.stdout.read()
obj.stdout.close()
cmd_error = obj.stderr.read()
obj.stderr.close()
print(cmd_out)
print(cmd_error)
示例3:
#encoding=utf-8
import subprocess
obj = subprocess.Popen
(["python"],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
obj.stdin.write(b"print(1)\n")
obj.stdin.write(b"print(2)\n")
obj.stdin.write(b"print(3)\n")
obj.stdin.write(b"print(4)\n")
out_info,out_error = obj.communicate()#返回一个元组,返回标准输出和错误输出
print(out_info)
print(out_error)
示例4,将一个子进程的输出,作为另一个子进程的输入:
#encoding=utf-8
import subprocess
child1 = subprocess.Popen(["ls","-al"],stdout=subprocess.PIPE)
child2 = subprocess.Popen(["wc","-l"],stdin=child1.stdout,stdout=subprocess.PIPE)
out = child2.communicate()#返回的是一个元组
print(out)
Subprocess进程通信实例
打开一个只有ip地址的文本文件,读取其中的ip或域名,然后进行ping操作,
并将ping结果写入ping.txt文件中。ip.txt文件内容如下:
www.baidu.com
www.taobao.com
123.45.5.34
127.0.0.1
#encoding=utf-8
import subprocess
import os
class Shell(object):
def runCmd(self,cmd):
res = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
#获取子进程的标准输出、标准错误信息
sout,serr = res.communicate()
#返回进程指定的结果码、输出信息、错误信息,子进程的编号
return res.returncode,sout,serr,res.pid
shell = Shell()
fp = open("e:\\ip.txt",encoding="utf-8")
ipList = fp.readlines()
fp.close()
fp = open("e:\\ping.txt","a",encoding="utf-8")
print(ipList)
for ip in ipList:
ip = ip.strip()
result = shell.runCmd("ping " + ip)
if result[0] == 0:
w = ip + ": 0"
else:
w = ip + ": 1"
fp.write(w + "\n")
print(result[1].decode("gbk"))
fp.close()
标准输入、输出、错误输出
>>> import sys
>>> a = sys.stdin.readline()
dddd
>>> a
'dddd\n'
>>> sys.stdout.write("bb")
bb2#输出字符个数
>>> sys.stderr.write("ddd")
3
ddd>>>
多进程(三)
原创
©著作权归作者所有:来自51CTO博客作者qq5a16e6241946e的原创作品,请联系作者获取转载授权,否则将追究法律责任

提问和评论都可以,用心的回复会被更多人看到
评论
发布评论
相关文章
-
java 多进程
一个项目分多进程目的是为了分解压力。
java 项目 -
Python多进程
关于Python多进程讲解
Python 多进程 Python 多进程 -
多进程续集
守护进程,互斥锁,队列(Queue),生产者消费者模型
守护进程互斥锁队列生产者消费者模型