python中多任务基本由进程,线程和协程完成。其中协程依托于线程,二线程又依赖于进程,进程是其中最消耗资源的方式。所以在使用多任务的时候要合理利用合适的方式来完成
import os
import multiprocessing
def test(arg):
print(arg)
print("当前进程id是%d"% os.getpid())
if __name__ == '__main__': # 必须使用
p1 = multiprocessing.Process(target=test,args=("aa",))
p2 = multiprocessing.Process(target=test,args=("cc",))
p1.start()
p2.start()
# 结果:
cc
当前进程id是3480
aa
当前进程id是20372
__name__ == '__main__'
语句,否则会报错报错如下:
RuntimeError:
An attempt has been made to start a new process before the
current process has finished its bootstrapping phase.
This probably means that you are not using fork to start your
child processes and you have forgotten to use the proper idiom
in the main module:
进程与进程之间是相互独立的.同一个程序中会有变量彼此调用,这个时候就需要用到进程之间的通信,也就用到了队列.
队列是先进先出,栈是先进后出
from multiprocessing import Queue
q = Queue(3) # 初始化一个Queue对象,可以最多接收三条消息
print(q.empty()) # 判断是否为空 # True
q.put("消息1")
q.put(111)
print(q.full()) # 判断队列是否满了 # False
print(q.empty()) # True
q.put([1,2,3])
print(q.full()) # True
print(q.get()) # 取数据 # 消息1
print(q.get()) # 111
print(q.get()) # [1,2,3]
import multiprocessing
from multiprocessing import Queue
def download_from_web(q):
data = [1,2,3,4]
# 向队列中写入数据
for temp in data:
q.put(temp)
print("下载数据完成并已存入队列")
def handle_data(q):
waitting_data = list()
while 1:
data = q.get()
x = data * 100
waitting_data.append(x)
if q.empty():
break
print(waitting_data)
def main():
# 1 创建一个队列
q = Queue()
# 2 创建进程,将队列的引用当做实参进行传递
p1 = multiprocessing.Process(target=download_from_web,args=(q,))
p2 = multiprocessing.Process(target=handle_data,args=(q,))
p1.start()
p2.start()
if __name__ == '__main__':
main()
# 结果:
下载数据完成并已存入队列
[100, 200, 300, 400]
一个程序中有多个函数或者多个功能模块,不可能每一个都单开一个进程,这时候为了资源的合理利用,就需要用到进程池,对进程重复利用,避免资源浪费
import time,os
from multiprocessing import Pool
def worker(msg):
t0 = time.time()
print("%s开始执行,进程号是%d" % (msg,os.getpid()))
time.sleep(2)
t1 = time.time()
print("消耗时间%0.2f"% (t1-t0))
if __name__ == '__main__': # 多进程需要在main函数中运行
# 创建进程池
P = Pool(3) # 定义一个进程池 3个进程循环使用
for i in range(0,10):
P.apply_async(worker,(i,)) # 异步非阻塞, 不用等待当前进程执行完毕,随时根据系统调度来进行进程切换
P.close() # 关闭进程池,不再接受新的请求
P.join() # 等待进程池中进程执行完毕,必须在close语句之后
# 结果:
0开始执行,进程号是5872
1开始执行,进程号是2892
2开始执行,进程号是5172
消耗时间2.00消耗时间2.00
3开始执行,进程号是5872
4开始执行,进程号是2892
消耗时间2.00
5开始执行,进程号是5172
消耗时间2.00消耗时间2.00
6开始执行,进程号是5872
7开始执行,进程号是2892
消耗时间2.00
8开始执行,进程号是5172
消耗时间2.00消耗时间2.00
9开始执行,进程号是2892
消耗时间2.00
消耗时间2.00
import time
import threading
def test(num):
print(num)
time.sleep(2)
def main():
t1 = threading.Thread(target=test,args=(7,))
t2 = threading.Thread(target=test,args=(8,)) # 逗号不能少
t1.start()
t2.start()
main() # 与多进程不同 直接调用main()函数也可以运行
线程之间共享资源,多线程的时候容易导致资源竞争,影响程序运行,所以在必要的时候需要使用互斥锁保证一个线程的完整执行
import threading
import time
g_num = 0
def test1(num):
global g_num
# 上锁,如果之前没有上锁,那么此时上锁
# 如果上锁之前 已经上过锁,那么堵塞在这里,直到解锁之后才重新上锁
mutex.acquire()
for i in range(num):
g_num += 1
# 解锁
mutex.release()
print("在test1里面g_num等于%d" % g_num)
def test2(num):
global g_num
mutex.acquire()
for i in range(num):
g_num += 1
mutex.release()
print("在test2里面g_num等于%d" % g_num)
# 创建一个互斥锁
mutex =threading.Lock()
def main():
t1 = threading.Thread(target=test1,args=(10000,))
t2 = threading.Thread(target=test2,args=(10000,))
t1.start()
t2.start()
if __name__ == '__main__':
main()
gevent模块是对greenlet的封装 基于yield的多任务,利用的是生成器中next方法完成
协程主要是利用程序中耗时的操作时 去执行其他的操作来完成多任务
import gevent
def fun(num):
for i in range(num):
print(gevent.getcurrent(),i)
g1 = gevent.spawn(fun,3)
g2 = gevent.spawn(fun,6)
g1.join()
g2.join()
在使用gevent时,是必须要把其他模块中的延时操作转换为gevent中的延时操作才可以是用协程的。为了方便转换,可以使用gevent中mokey进行打补丁操作,直接转换,同时其他模块的延时照常使用
import time
import gevent
from gevent import monkey
# 给程序打包
monkey.patch_all() # 将程序中用到的耗时操作,换成gevent中自己实现的代码
def fun(num):
for i in range(num):
print(gevent.getcurrent(),i)
time.sleep(1)
# g1 = gevent.spawn(fun,3)
# g2 = gevent.spawn(fun,6)
# g1.join()
# g2.join()
gevent.joinall([
gevent.spawn(fun,3),
gevent.spawn(fun,6)
]) # 注意这里的对象需要用列表包裹