python 并发编程
1.创建进程
- 使用Process()来创建进程
from multiprocessing import Process
import time
def func(name):
print(f'{name}进程开始')
time.sleep(5)
print(f'{name}任务执行完毕')
if __name__ == '__main__':
p = Process(target=func, args=('写讲话稿',))
p.start() #启动进程
print("主进程")

可以利用类来定义自己的进程
class MyProcess(Process):
def __init__(self,name):
super().__init__()
self.task_name=name
def run(self):
print(f'{self.task_name}任务开始')
time.sleep(3)
print("任务结束")
pc1 = MyProcess("玩原神")
pc1.run()

from multiprocessing import Process
import time
def func(name,i):
print(f'{name}进程开始')
time.sleep(i)
print(f'{name}任务执行完毕')
if __name__ == '__main__':
start = time.time() #记录当前时间
l=[] #用于存放进程对象
'''
对于序列中的每个整数,创建一个新的进程。Process是Python的multiprocessing模块中的一个类,用于创建新的进程。target参数指定了要在新进程中运行的函数,这里是func函数。args参数是一个元组,包含了要传递给func函数的参数。在这个例子中,参数是一个字符串f'写演讲稿{i}'和整数i,其中i是循环变量。
'''
for i in range(1,4):
p = Process(target=func ,args=(f'写演讲稿{i}',i))
p.start()
p.join()
for p in l:
p.join()
print('主进程')
end = time.time() #记录结束时间
print(end - start)
等待进程结束。join()方法会阻塞当前线程,直到调用该方法的进程结束。

2.进程对象的其他方法
from multiprocessing import Process,current_process
import os
import time
def task():
print(f'任务{current_process().pid}执行中')
time.sleep(5)
if __name__ == '__main__':
p = Process(target=task)
p.start()
print("主进程",current_process().pid)

3.僵尸进程与孤儿进程
僵尸进程是指在计算机操作系统中,子进程已经结束运行,但其父进程尚未对其进行资源回收的进程状态
孤儿进程是指在计算机操作系统中,一个父进程已经终止或退出,但其子进程仍在运行的进程
# coding:utf-8
from multiprocessing import Process
import time
def task():
print("任务开始")
time.sleep(4)
print("任务结束")
if __name__ == '__main__':
p=Process(target=task)
p.start()
print("主进程")

4.守护进程
from multiprocessing import Process
import time
def task(name):
print(name,'还活着')
time.sleep(3)
print(name,'正常死亡')
if __name__ == '__main__':
p = Process(target=task , kwargs={'name':'苏妲己'})
p.daemon = True #将进程p设置为守护进程
p.start()
time.sleep(1)
print('纣王驾崩了')
设置守护进程后,一旦父进程结束则子进程立刻结束

再看看如果没有设置守护进程的情况
from multiprocessing import Process
import time
def task(name):
print(name,'还活着')
time.sleep(3)
print(name,'正常死亡')
if __name__ == '__main__':
p = Process(target=task , kwargs={'name':'苏妲己'})
p.start()
time.sleep(1)
print('纣王驾崩了')

5.互斥锁
互斥锁(Mutex,全称 Mutual Exclusion)是一种同步原语,用于在多线程编程中保护共享资源,确保同一时刻只有一个线程可以访问该资源
下面模拟一个买票的过程
加上互斥锁可以避免吞票情况,保证票是一个个减少的
from multiprocessing import Process,Lock
import time
import json
import random
#查票
def search_ticket(name):
with open(r'D:\Pycharm workplace\python全栈高级篇\第二章 并发编程\tickets','r',encoding='utf-8') as f:
dic = json.load(f)
print(f'用户{name}查询余票:{dic.get("tickets_num")}')
#买票
def buy_ticket(name):
with open(r'D:\Pycharm workplace\python全栈高级篇\第二章 并发编程\tickets','r',encoding='utf-8') as f:
dic = json.load(f)
#模拟网络延迟
time.sleep(random.randint(1,5))
if dic.get('tickets_num') >0:
dic['tickets_num']-=1
with open(r'D:\Pycharm workplace\python全栈高级篇\第二章 并发编程\tickets','w',encoding='utf-8') as f:
json.dump(dic,f)
print(f'用户{name}买票成功')
else:
print(f"余票不足,用户{name}买票失败")
def task(name,mutex):
search_ticket(name)
#抢锁
mutex.acquire()
buy_ticket(name)
#释放锁
mutex.release()
if __name__ == '__main__':
mutex = Lock()
for i in range(1,9):
p = Process(target=task,args=(i,mutex))
p.start()
tickets文件的内容
开始时:
{"tickets_num": 5}
结束后:
{"tickets_num": 0}

6.消息队列
import queue
q=queue.Queue(6)
q.put('a')
q.put('b')
q.put('c')
q.put('d')
q.put('e')
q.put('f')
q1=q.get()
q2=q.get()
q3=q.get()
q4=q.get()
q5=q.get()
q6=q.get()
print(q1,q2,q3,q4,q5,q6)
队列特点:先进先出

7.IPC机制
IPC(Inter-Process Communication,进程间通信)机制是操作系统中用于实现不同进程之间数据交换和同步的通信方法
from multiprocessing import Process,Queue
def task1(q):
q.put('宫保鸡丁')
def task2(q):
print(q.get())
if __name__ == '__main__':
q = Queue()
p1 = Process(target=task1 , args=(q,))
p2 = Process(target=task2 , args=(q,))
p1.start()
p2.start()
8.生产者消费者模型
生产者:生成或制造数据的
消费者:消费或处理数据的
import random
import time
from multiprocessing import Process,Queue,JoinableQueue
def producer(name,food,q):
for i in range(8):
time.sleep(random.randint(1, 3))
print(f"{name}做了{food}{i}")
q.put(f'{food}{i}')
def consumer(name,q):
while True:
food = q.get()
time.sleep(random.randint(1,3))
print(f"{name}吃了{food}")
q.task_done()
if __name__ == '__main__':
q = JoinableQueue()
p1 = Process(target=producer , args=('中华小当家','黄金炒饭',q))
p2 = Process(target=producer, args=('神厨小福贵', '佛跳墙',q))
c1 = Process(target=consumer , args=('八戒',q))
c2 = Process(target=consumer , args=('悟空',q))
c1.daemon = True
c2.daemon = True
p1.start()
p2.start()
c1.start()
c2.start()
p1.join()
p2.join()
q.join()import random
import time
from multiprocessing import Process,Queue,JoinableQueue
def producer(name,food,q):
for i in range(8):
time.sleep(random.randint(1, 3))
print(f"{name}做了{food}{i}")
q.put(f'{food}{i}')
def consumer(name,q):
while True:
food = q.get()
time.sleep(random.randint(1,3))
print(f"{name}吃了{food}")
# if food == '鹤顶红':
# break
q.task_done()
if __name__ == '__main__':
q = JoinableQueue()
p1 = Process(target=producer , args=('中华小当家','黄金炒饭',q))
p2 = Process(target=producer, args=('神厨小福贵', '佛跳墙',q))
c1 = Process(target=consumer , args=('八戒',q))
c2 = Process(target=consumer , args=('悟空',q))
c1.daemon = True
c2.daemon = True
p1.start()
p2.start()
c1.start()
c2.start()
p1.join()
p2.join()
q.join()
输出如下
神厨小福贵做了佛跳墙0
中华小当家做了黄金炒饭0
八戒吃了佛跳墙0
悟空吃了黄金炒饭0
神厨小福贵做了佛跳墙1
中华小当家做了黄金炒饭1
神厨小福贵做了佛跳墙2
八戒吃了佛跳墙1
神厨小福贵做了佛跳墙3
悟空吃了黄金炒饭1
中华小当家做了黄金炒饭2
神厨小福贵做了佛跳墙4
悟空吃了佛跳墙3
八戒吃了佛跳墙2
八戒吃了佛跳墙4
中华小当家做了黄金炒饭3
神厨小福贵做了佛跳墙5
悟空吃了黄金炒饭2
中华小当家做了黄金炒饭4
神厨小福贵做了佛跳墙6
八戒吃了黄金炒饭3
神厨小福贵做了佛跳墙7
悟空吃了佛跳墙5
中华小当家做了黄金炒饭5
悟空吃了佛跳墙6
八戒吃了黄金炒饭4
中华小当家做了黄金炒饭6
悟空吃了佛跳墙7
八戒吃了黄金炒饭5
中华小当家做了黄金炒饭7
悟空吃了黄金炒饭6
八戒吃了黄金炒饭7
9.线程
进程:资源单位
线程:执行单位
创建进程
- 申请内存空间
- 拷贝代码,消耗资源
创建线程:在一个进程内可以创建多个线程,同一个进程内,多个线程之间的资源是共享的
- 不需要再次申请内存空间
- 不需要拷贝代码
import time
from multiprocessing import Process
from threading import Thread
import time
def task(name):
print(f"{name} 任务开始")
time.sleep(3)
print(f'{name} 任务结束')
if __name__ == '__main__':
t=Thread(target=task ,args=('悟空',))
t.start()
print("主线程")

将线程换成进程看看
import time
from multiprocessing import Process
from threading import Thread
import time
def task(name):
print(f"{name} 任务开始")
time.sleep(3)
print(f'{name} 任务结束')
if __name__ == '__main__':
t=Process(target=task ,args=('悟空',))
t.start()
print("主线程")

不难发现,创建进程的时候主进程先执行,而创建线程的时候线程比主线程先执行,由此可知,创建线程相比创建进程来说速度更快,资源消耗更低
另一种创建线程的方式
class MyThread(Thread):
def __init__(self,name):
super().__init__()
self.name = name
def run(self):
print(f"{self.name} 任务开始")
time.sleep(3)
print(f'{self.name} 任务结束')
if __name__ == '__main__':
t=MyThread('悟空')
t.start()
print("主线程")
10.tcp服务端和客户端
服务端
import socket
from multiprocessing import Process
from threading import Thread
def task(conn):
while True:
try:
data = conn.recv(1024)
except:
break
if not data:
break
print(data.decode('utf-8'))
conn.send(data.upper())
conn.close()
if __name__ == '__main__':
s = socket.socket()
s.bind(('127.0.0.1', 8005))
s.listen(5)
while True:
conn,addr=s.accept()
p =Thread(target=task , args=(conn,))
p.start()
客户端
import socket
import time
c = socket.socket()
c.connect(('127.0.0.1',8005))
while True:
c.send(b'hello')
data = c.recv(1024)
print(data.decode('utf-8'))
time.sleep(2)
11 GIL全局解释器
GIL(Global Interpreter Lock,全局解释器锁)是CPython解释器中的一种机制,它确保同一时间只有一个线程执行Python字节码
问题:python的多进程好像没用,无法利用多核优势,即便有多个核,一次也只能用一个
python的解释器版本:
- Cpython
- Jpython
- Pypypython
注意:
- GIL不是python的特点,是Cpython解释器独有的特点
- GIL保证的是解释器级别数据的安全
- 我们写代码时候,该怎么写怎么写,不用考虑GIL
import time
from threading import Thread,Lock
num=180
mutex = Lock()
def task():
global num
mutex.acquire()
temp =num
time.sleep(0.05)
num = temp -1
mutex.release()
if __name__=='__main__':
l = []
for i in range(180):
t = Thread(target=task)
t.start()
l.append(t)
for t in l:
t.join()
print(num)

import time
from threading import Thread,Lock
num=180
mutex = Lock()
def task():
global num
temp =num
num = temp -1
if __name__=='__main__':
l = []
for i in range(180):
t = Thread(target=task)
t.start()
l.append(t)
for t in l:
t.join()
print(num)

import time
from threading import Thread,Lock
num=180
mutex = Lock()
def task():
global num
# mutex.acquire()
temp =num
time.sleep(0.05)
num = temp -1
# mutex.release()
if __name__=='__main__':
l = []
for i in range(180):
t = Thread(target=task)
t.start()
l.append(t)
for t in l:
t.join()
print(num)

12. 多进程 vs 多线程
计算密集型
- 多进程
from multiprocessing import Process
from threading import Thread
import time
#计算密集型
def task():
res=0
for i in range(100000):
res+=i
if __name__ == '__main__':
l=[]
start=time.time()
for i in range(100):
p = Process(target=task)
p.start()
for p in l:
p.join()
end = time.time()
print(end-start)
22.814913988113403
- 多线程
from multiprocessing import Process
from threading import Thread
import time
def task():
res=0
for i in range(100000):
res+=i
if __name__ == '__main__':
l=[]
start=time.time()
for i in range(100):
p = Thread(target=task)
p.start()
for p in l:
p.join()
end = time.time()
print(end-start)
0.3315262794494629
说明计算密集型,多进程更有优势
而IO密集型,多线程更有优势
总结:
- 多进程和多线程都有各自的优势,以后写项目时候通常可以多进程下开设多线程
- 我们现在开发的程序,90%以上,其实都是IO密集型,多线程优势更大
- 多进程使用场景(挖矿,训练ai,解决三体问题等等),利用cpu的多核能力

浙公网安备 33010602011771号