因为全局解释器锁的存在,在python里经常使用多进程来代替多线程。
可以避免受GIL的限制
一、进程的创建
进程创建有如下三种方式:
1、通过multiprocessing下Process类创建子进程;
2、继承Process类,重写run方法
3、进程池
需要注意的是:进程一定要写在“主程序”中,因为windows系统在创建子进程的时候,会将当前的模块导入,如直接写在主程序中,会报错,如下所示:
import multiprocessing
def m():
pass
p1 = multiprocessing.Process(target=m)
p1.start()
报错:
RuntimeError:
An attempt has been made to start a new process before the
current process has finished its bootstrapping phase.
正确执行方法为:
import multiprocessing,os
def m():
print("子进程执行")
print("子进程执行时ID是{},此时子进程中父进程ID是{}".format(os.getpid(),os.getppid()))
if __name__ == "__main__":
p1 = multiprocessing.Process(target=m)
p1.start()
print("父进程执行时ID是{},此时父进程中的父进程ID是{}".format(os.getpid(),os.getppid()))
输出:
父进程执行时ID是4600,此时父进程中的父进程ID是15456
子进程执行
子进程执行时ID是10292,此时子进程中父进程ID是4600
二、进程的相关操作
os.getpid():获得当前进程
os.getppid():获得当前进程的父进程
os.fork:复制进程(在windows系统下不支持)
其他方法:run、start、is_alive、name、daemon
三、进程队列
两个进程之间的区域不共享,所以进程不存在进程不安全的问题;
但是进程队列需要解决资源的通信问题;
pickle序列化解决进程之间的资源通信(同步)
在向一个进程中修改队列的时候,同时向一个进程使用pickle 序列化的方式同步队列的信息
在另外一个进程中将序列化的信息再恢复成对象
四、线程的通知和等待
wait:等待并释放当前线程占用的锁
notify:任选一个等待线程
notifyall:通知所有等待的线程
具体用法可通过如下案例实现。
需求:有生产者和消费者两个对象,仓库只能最多容纳3个产品,生产者生产大于3时不在生产,消费者消费到0后不再消费。
代码实现如下:
import threading,time
lock = threading.Condition()
def produce(li):
for i in range(10):
try:
lock.acquire()
if len(li)==3:
print("仓库已满,生产阻塞")
lock.wait()
else:
li.append("goods")
print("{}生产了{}".format(i,li[-1]))
lock.notify_all()
finally:
lock.release()
def consume(li):
for i in range(10):
try:
lock.acquire()
if len(li)==0:
print("仓库为0,消费阻塞")
lock.wait()
else:
print("{}消费了{}".format(i,li[-1]))
li.pop(0)
lock.notify_all()
finally:
lock.release()
if __name__ =="__main__":
li=[]
t1 = threading.Thread(target=produce,args=(li,))
t2 = threading.Thread(target=consume,args=(li,))
t1.start()
t2.start()
输出:
0生产了goods
1生产了goods
2生产了goods
仓库已满,生产阻塞
0消费了goods
1消费了goods
2消费了goods
仓库为0,消费阻塞
4生产了goods
5生产了goods
6生产了goods
仓库已满,生产阻塞
4消费了goods
5消费了goods
6消费了goods
仓库为0,消费阻塞
8生产了goods
9生产了goods
8消费了goods
9消费了goods
五、线程队列
队列内部实现了锁机制。开发时候,并发操作都是会用队列完成。
使用queue Queue来创建队列的对象
有如下几种队列:
1、先进先出队列:queue.Queue()
2、先进后出(堆栈):queue.LifoQueue()
3、优先队列:queue.PriorityQueue()
先进先出队列:
queue.Queue(maxsize):maxsize指定队列的最大容量,如果maxsize不写,代表无限容量
q=queue.Queue()
q.qsize()表示队列中元素的个数
q.empty()判断队列是否为空:空为Ture,否则为False
q.full()判断队列是否为满
q.put():向队列中放元素
block阻塞状态:默认值是True 当队列已满,继续put,会使得程序阻塞
timeout:指定等待阻塞的时间, 必须block=True才有效
q.put_nowait():当向队列中put元素,如果队列已满,不等待,相当于执行 q.put(“str”,block=False)
q.get():从队列中取元素,get之后,元素就没有了
block阻塞状态:默认是True,当队列已空,继续get,则会使得程序阻塞,timeout:指定阻塞的时间,必须block=True才有效
q.get_nowait():当向队列中get元素,如果队列已满,不等待,相当于执行 q.get(“str”,block=False)
相关应用如下:
import queue
q = queue.Queue()
q.put("a")
q.put("b")
q.put("c")
print(q.empty(),q.full(),q.qsize())
print(q.get(block=True,timeout=1))
print(q.get_nowait())
输出:
False False 3
a
b
先进后出队列(堆栈):queue.LifoQueue()
相关方法与先进先出队列一样
import queue
q = queue.LifoQueue()
q.put("a")
q.put("b")
q.put("c")
while not q.empty():
print(q.get())
输出:
c
b
a
优先队列
根据元素的优先原则排列,出队列
import queue
q = queue.PriorityQueue()
q.put("3a")
q.put("1b")
q.put("2c")
while not q.empty():
print(q.get())
输出:
1b
2c
3a
队列内部实现了锁机制,队列是线程安全的,可以多个线程共享变量
可用队列来实现刚刚生产者消费者的案例
import queue,threading
def produce(q,name):
i = 0
while True:
q.put(i)
print("{}生产{},仓库还有{}".format(name,i,q.qsize()))
i+=1
def consume(q,name):
while True:
print("{}消费{},目前还有{}".format(name,q.get(),q.qsize()))
if __name__ == "__main__":
q = queue.Queue(3)
t1 = threading.Thread(target=produce,args=(q,"zhangsan"))
t2 = threading.Thread(target=consume, args=(q,"lisi"))
t3 = threading.Thread(target=consume, args=(q,"wangwu"))
t1.start()
t2.start()
t3.start()
输出结果片段如下:
zhangsan生产1779,仓库还有3
lisi消费1777,目前还有2
wangwu消费1778,目前还有1
wangwu消费1779,目前还有0
zhangsan生产1780,仓库还有1
lisi消费1780,目前还有0
zhangsan生产1781,仓库还有1
使用进程实现:
import multiprocessing,time,random,os
def produce(q):
li = ["苹果","香蕉","梨"]
while True:
a = random.choice(li)
q.put(a)
print("进程{}生产了{}".format(os.getpid(),a))
time.sleep(0.1)
def comsue(q):
while True:
print("进程{}消费了{}".format(os.getpid(),q.get()))
time.sleep(0.2)
if __name__ == "__main__":
q = multiprocessing.Queue(5)
m1 = multiprocessing.Process(target=produce,args=(q,))
m2 = multiprocessing.Process(target=comsue, args=(q,))
m1.start()
m2.start()
输出:
进程2568生产了梨
进程18100消费了梨
进程2568生产了香蕉
进程2568生产了梨
进程18100消费了香蕉
进程2568生产了苹果