Python-多任务-进程
Python-多任务-进程
一 什么是进程
进程:是资源分配的基本单位。当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。CPU是以时间片的方式为进程分配CUP处理时间。
而一个进程又是由多个线程所组成的。
线程:是程序执行流的最小单元,是系统独立调度和分配CPU(独立运行)的基本单位。线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。线程是进程中的最小的执行单位,进程想要执行任务就需要依赖线程,并且一个进程中至少有一个线程。
程序:例如xxx.py这是程序,是一个静态的
二 进程的过程
1 进程的状态
- 就绪态:运行的条件都已经满足,正在等在cpu执行
- 执行态:cpu正在执行其功能
- 等待态:等待某些条件满足,例如一个程序sleep了,此时就处于等待态
三 进程的特点/优缺点
1 进程和线程的对比
定义的不同
- 进程是系统进行资源分配和调度的一个独立单位.
- 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
区别
- 一个程序至少有一个进程,一个进程至少有一个线程.
- 线程的划分尺度小于进程(资源比进程少),使得多线程程序的并发性高。
- 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率
2 优缺点
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。
四 Python使用多进程
1 多进程简单使用
from multiprocessing import Process
import time
def process1():
for i in range(10):
time.sleep(1)
print("process%s1:" % ("-"*5), i)
print("process%s1Done" % ("-" * 5))
if __name__ == '__main__':
p = Process(target=process1)
p.start()
for i in range(10):
time.sleep(1)
print("process%smain:" % ("-"*5), i)
print("process%smainDone" % ("-" * 5))
2 获取进程id
from multiprocessing import Process
import time
import os
def process1():
for i in range(10):
time.sleep(1)
print("process%s1:" % ("-"*5), i, [os.getpid()]) # 使用os模块获取进程id
print("process%s1Done" % ("-" * 5))
if __name__ == '__main__':
p = Process(target=process1)
p.start()
for i in range(10):
time.sleep(1)
print("process%smain:" % ("-"*5), i, [os.getpid()]) # 使用os模块获取进程id
print("process%smainDone" % ("-" * 5))
3 Process语法结构及常用方法
Process语法结构:
Process([group [, target [, name [, args [, kwargs]]]]])
- target:如果传递了函数的引用,这个子进程的任务就是执行这里的代码
- args:给target指定的函数传递的参数,以元组的方式传递
- kwargs:给target指定的函数传递命名参数
- name:给进程设定一个名字,可以不设定
- group:指定进程组,大多数情况下用不到
Process创建的实例对象的常用方法:
- start():启动子进程实例(创建子进程)
- is_alive():判断进程子进程是否还在活着
- join([timeout]):是否等待子进程执行结束,或等待多少秒
- daemon:守护进程,默认为False;设置为True后(要在start()前设置),主进程完成后,不管子进程走到哪里,都要停止并退出
- terminate():不管任务是否完成,立即终止子进程
Process创建的实例对象的常用属性:
- name:当前进程的别名,默认为Process-N,N为从1开始递增的整数
- pid:当前进程的pid(进程号)
from multiprocessing import Process
import time
import os
def process1():
for i in range(10):
time.sleep(1)
print("process%s1:" % ("-"*5), i, [os.getpid()])
print("process%s1Done" % ("-" * 5))
if __name__ == '__main__':
p = Process(target=process1)
p.start()
print("子进程的name:%s;进程id:%d" % (p.name, p.pid))
print("子进程是否存活:%s" % p.is_alive()) # 子进程是否存活
# p.join() # 在这里阻塞,直到子进程执行完成,可以设置时间
p.terminate() # 不管任务是否完成,立即终止子进程,但是结束的时候需要一点时间,不然p.is_alive()还是True
time.sleep(1)
print("子进程的name:%s;进程id:%d" % (p.name, p.pid))
print("子进程是否存活:%s" % p.is_alive()) # 子进程是否存活
# for i in range(10):
# time.sleep(1)
#
# print("process%smain:" % ("-"*5), i, [os.getpid()])
print("process%smainDone" % ("-" * 5))
4 给子进程指定的函数传递参数
from multiprocessing import Process
import time
import os
def process1(name, age, **kwargs):
for i in range(10):
time.sleep(1)
print("process%s1:" % ("-"*5), i, [os.getpid()])
print(name, age, kwargs)
print("process%s1Done" % ("-" * 5))
if __name__ == '__main__':
p = Process(target=process1, args=("chen", 18), kwargs={"key1": "value1", "key2": "value2"})
p.start()
print("子进程的name:%s;进程id:%d" % (p.name, p.pid))
print("子进程是否存活:%s" % p.is_alive()) # 子进程是否存活
# p.join() # 在这里阻塞,直到子进程执行完成,可以设置时间
# p.terminate() # 不管任务是否完成,立即终止子进程,但是结束的时候需要一点时间,不然p.is_alive()还是True
time.sleep(1)
print("子进程的name:%s;进程id:%d" % (p.name, p.pid))
print("子进程是否存活:%s" % p.is_alive()) # 子进程是否存活
# for i in range(10):
# time.sleep(1)
#
# print("process%smain:" % ("-"*5), i, [os.getpid()])
print("process%smainDone" % ("-" * 5))
5 进程间不共享全局变量
from multiprocessing import Process
import time
import os
test_list = [1, 2, 3, 4, 5]
def process1(name, test_list):
test_list.append(name)
print(id(test_list), name, ":", test_list)
print("process%s1Done" % ("-" * 5))
if __name__ == '__main__':
p1 = Process(target=process1, args=("chen", test_list))
p1.start()
p2 = Process(target=process1, args=("jun", test_list))
p2.start()
p3 = Process(target=process1, args=("ming", test_list))
p3.start()
p1.join()
p2.join()
p3.join()
print(id(test_list), "main:", test_list)
print("process%smainDone" % ("-" * 5))
"""
44719880 chen : [1, 2, 3, 4, 5, 'chen']
process-----1Done
44916488 jun : [1, 2, 3, 4, 5, 'jun']
process-----1Done
44392200 ming : [1, 2, 3, 4, 5, 'ming']
process-----1Done
44107272 main: [1, 2, 3, 4, 5]
process-----mainDone
"""
线程对比
from threading import Thread
import time
import os
test_list = [1, 2, 3, 4, 5]
def process1(name, test_list):
test_list.append(name)
print(id(test_list), name, ":", test_list)
print("process%s1Done" % ("-" * 5))
if __name__ == '__main__':
p1 = Thread(target=process1, args=("chen", test_list))
p1.start()
p2 = Thread(target=process1, args=("jun", test_list))
p2.start()
p3 = Thread(target=process1, args=("ming", test_list))
p3.start()
p1.join()
p2.join()
p3.join()
print(id(test_list), "main:", test_list)
print("process%smainDone" % ("-" * 5))
"""
44006600 chen : [1, 2, 3, 4, 5, 'chen']
process-----1Done
44006600 jun : [1, 2, 3, 4, 5, 'chen', 'jun']
process-----1Done
44006600 ming : [1, 2, 3, 4, 5, 'chen', 'jun', 'ming']
process-----1Done
44006600 main: [1, 2, 3, 4, 5, 'chen', 'jun', 'ming']
process-----mainDone
"""
另一个例子
import threading
import time
from multiprocessing import Process
g_num = 20
def work1(num):
global g_num
for i in range(num):
g_num += 1
print("----in work1, g_num is %d---"%g_num)
def work2(num):
global g_num
for i in range(num):
g_num += 1
print("----in work2, g_num is %d---"%g_num)
if __name__ == '__main__': #多进程需要在main函数中运行
print("---进程创建之前g_num is %d---"%g_num)
t1 = Process(target=work1, args=(1000000,))
t1.start()
t2 = Process(target=work2, args=(1000000,))
t2.start()
time.sleep(5)
print("2个进程对同一个全局变量操作之后的最终结果是:%s" % g_num)
"""
---进程创建之前g_num is 20---
----in work1, g_num is 1000020---
----in work2, g_num is 1000020---
2个进程对同一个全局变量操作之后的最终结果是:20
"""
6 进程间通信——Queue
初始化Queue()对象时(例如:q=Queue()),若括号中没有指定最大可接收的消息数量,或数量为负值,那么就代表可接受的消息数量没有上限(直到内存的尽头);
-
Queue.qsize():返回当前队列包含的消息数量;
-
Queue.empty():如果队列为空,返回True,反之False ;
-
Queue.full():如果队列满了,返回True,反之False;
-
Queue.get([block[, timeout]]):获取队列中的一条消息,然后将其从列队中移除,block默认值为True;
- 1)如果block使用默认值,且没有设置timeout(单位秒),消息列队如果为空,此时程序将被阻塞(停在读取状态),直到从消息列队读到消息为止,如果设置了timeout,则会等待timeout秒,若还没读取到任何消息,则抛出"Queue.Empty"异常;
- 2)如果block值为False,消息列队如果为空,则会立刻抛出"Queue.Empty"异常;
-
Queue.get_nowait():相当Queue.get(False);
-
Queue.put(item,[block[, timeout]]):将item消息写入队列,block默认值为True;
- 1)如果block使用默认值,且没有设置timeout(单位秒),消息列队如果已经没有空间可写入,此时程序将被阻塞(停在写入状态),直到从消息列队腾出空间为止,如果设置了timeout,则会等待timeout秒,若还没空间,则抛出"Queue.Full"异常;
- 2)如果block值为False,消息列队如果没有空间可写入,则会立刻抛出"Queue.Full"异常;
-
Queue.put_nowait(item):相当Queue.put(item, False);
import time
from multiprocessing import Process
from multiprocessing import Queue
g_num = 20
# g_list = [1, 2, 3, 4]
q = Queue(3) # 初始化一个Queue对象,最多可接收4条put消息
def work1(q, num):
g_num = 0
for i in range(num):
g_num = q.get()
g_num += 1
q.put(g_num)
print("----in work1, g_num is %d---"%g_num)
def work2(q, num):
g_num = 0
for i in range(num):
g_num = q.get()
g_num += 1
q.put(g_num)
print("----in work2, g_num is %d---"%g_num)
if __name__ == '__main__': #多进程需要在main函数中运行
print("---进程创建之前g_num is %d---"%g_num)
q.put(g_num)
t1 = Process(target=work1, args=(q, 100000))
t1.start()
t2 = Process(target=work2, args=(q, 100000))
t2.start()
while t1.is_alive() or t2.is_alive():
pass
g_num = q.get()
print("2个进程对同一个全局变量操作之后的最终结果是:%s" % g_num)
"""
---进程创建之前g_num is 20---
----in work2, g_num is 200002---
----in work1, g_num is 200020---
2个进程对同一个全局变量操作之后的最终结果是:200020
"""
7 进程池——Pool
当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态成生多个进程,但如果是上百甚至上千个目标,手动的去创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法。
初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务
multiprocessing.Pool常用函数解析:
- apply_async(func[, args[, kwds]]) :使用非阻塞方式调用func(并行执行,堵塞方式必须等待上一个进程退出才能执行下一个进程),args为传递给func的参数列表,kwds为传递给func的关键字参数列表;
- close():关闭Pool,使其不再接受新的任务;
- terminate():不管任务是否完成,立即终止;其中close()跟terminate()的区别在于close()会等待池中的worker进程执行结束再关闭pool,而terminate()则是直接关闭
- join():主进程阻塞,等待子进程的退出, 必须在close()或terminate()之后使用;
注意:如果要使用Pool创建进程,就需要使用multiprocessing.Manager()中的Queue(),而不是multiprocessing.Queue(),否则会得到一条如下的错误信息:
RuntimeError: Queue objects should only be shared between processes through inheritance.
import time
# from multiprocessing import Process
# from multiprocessing import Queue
from multiprocessing import Pool, Manager
g_num = 20
# g_list = [1, 2, 3, 4]
def work(q, num, time_num):
start_time = time.time()
g_num = 0
for i in range(num):
g_num = q.get()
g_num += 1
q.put(g_num)
end_time = time.time()
print("----in work%d, g_num is %d---耗时:%0.3f" % (time_num, g_num, end_time-start_time))
if __name__ == '__main__': # 多进程需要在main函数中运行
q = Manager().Queue(3) # 初始化一个Queue对象,最多可接收3条put消息
print("---进程创建之前g_num is %d---"%g_num)
q.put(g_num, False)
pool = Pool(3)
for i in range(10):
result = pool.apply_async(work, (q, 1000, i))
print(result)
print(pool._cache) # 查看进程的数量
# pool.terminate() # 其中close()跟terminate()的区别在于close()会等待池中的worker进程执行结束再关闭pool,而terminate()则是直接关闭
pool.close()
pool.join() # 主进程阻塞,等待子进程的退出, 必须在close()或terminate()之后使用
print(result.successful()) # 没有完成会抛出异常。完成为True
print("-"*150)
print(pool._cache) # 查看进程的数量
while len(pool._cache) > 0:
pass
g_num = q.get()
print("所有进程对同一个全局变量操作之后的最终结果是:%s" % g_num)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构