Python多任务-进程

我们可以这样理解进程与线程,进程相当于是电脑上运行的qq,qq音乐,lol这样的不同的程序,而线程是qq上的不同的聊天窗口。根本区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位
而进程和程序的差别则是:
进程:正在执行的程序
程序:没有执行的代码,是一个静态的
使用进程实现多任务
multiprocessing模块就是跨平台的多进程模块,提供了一个Process类来代表一个进程对象,这个对象可以理解为是一个独立的进程,可以执行另外的事情。

import multiprocessing


def sing():
    for i in range(3):
        print('正在唱歌...%s' % i)


def dance():
    for i in range(3):
        print('正在跳舞...%s' % i)


def main():
    p1 = multiprocessing.Process(target=sing)
    p2 = multiprocessing.Process(target=dance)

    p1.start()
    p2.start()


if __name__ == '__main__':
    main()

和线程的代码大致相同,就是将几个位置改一下。

进程间通信-Queue

import multiprocessing


a = 1

def demo1():
    global a
    a += 1


def demo2():
# 进程是不共享的   线程是共享
    print(a)


if __name__ == '__main__':
    t1 = multiprocessing.Process(target=demo1)
    t2 = multiprocessing.Process(target=demo2)

    t1.start()
    t2.start()

运行上述代码我们会发现打印出的结果为1,可知共享全局变量不适用于多进程编程
要完成进程间的通信,我们需要用到队列Queue,进程中的队列用法:

from multiprocessing import Queue

# 创建队列  最多可以存放3条数据
q = Queue(3)

# 存数据
q.put(1)
q.put("juran")
q.put([11, 22])

# print(q.qsize()) 查看队列长度

# q.put({"name": "juran"})  此时队列已满会堵塞

# q.put_nowait({"name": "juran"})  使用nowait的话会直接抛出异常
# print(q.full()) 查看队列是否满了

print(q.get())  # 从队列里取值
print(q.get())
print(q.get())


# 堵塞
# print(q.get())
# print(q.get_nowait())



# print(q.empty())  查看队列是否为空

队列遵循的是先进先出原则。

import multiprocessing
from multiprocessing import Queue


def demo1(q):
    q.put('a')


def demo2(q):
    data = q.get()
    print(data)


if __name__ == '__main__':
    q = Queue()

    t1 = multiprocessing.Process(target=demo1, args=(q,))
    t2 = multiprocessing.Process(target=demo2, args=(q,))
    t1.start()
    t2.start()

如上述代码引入进程中的Queue队列并创建一个队列,就能达到进程间通信的目的,值得注意的是,这里用的是进程的通信,即需要从multiprocessing库中导入的Queue,而不是从queue中导入的Queue。

进程池

当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态生成多个进程,但是如果是上百甚至上千个目标,手动的去创建的进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法

初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求,但是如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务

from multiprocessing import Pool
import os, time, random


def worker(msg):
    t_start = time.time()
    print('%s开始执行,进程号为%d' % (msg, os.getpid()))

    time.sleep(random.random() * 2)
    t_stop = time.time()
    # 0.2f
    print(msg, "执行完成,耗时%0.2f" % (t_stop - t_start))


if __name__ == '__main__':
    po = Pool(4)  # 定义一个进程池  4个进程

    for i in range(0, 10):
        po.apply_async(worker, (i,))

    print("--start--")
    # 关闭进程池 不在接收新的请求
    po.close()

    # 等待子进程执行完成
    po.join()
    print("--end--")
   

我们通过multiprocessing中的Pool来创建进程池,括号中的参数为创建的进程个数,因为代码执行效率高,所以我们给代码的运行添加一点延迟方便观察结果,最后通过代码运行的结果
在这里插入图片描述
可以发现进程号就在4个里面变化,当一个进程号里的进程运行结束,就运行下一个进程。

进程池间的进程通信

import multiprocessing


def demo1(q):
    # 进程池里面的进程 如果出现异常 不会主动抛出
    try:
        q.put('a')
    except Exception as e:
        print(e)


def demo2(q):
    try:
        data = q.get()
        print(data)
    except Exception as e:
        print(e)


if __name__ == '__main__':
    # q = Queue()  不能完成进程之间的通信
    # q = multiprocessing.Queue()   # 进程间通信
    q = multiprocessing.Manager().Queue()       # 进程池中的进程通信

    po = multiprocessing.Pool(2)

    po.apply_async(demo1, args=(q,))
    po.apply_async(demo2, args=(q,))

    po.close()

    po.join()

进程池间的进程通信也是通过队列来完成,不过与进程间通信使用的队列有所不同,总结如下。
q = Queue() 不能完成进程之间的通信
q = multiprocessing.Queue() # 进程间通信
q = multiprocessing.Manager().Queue() # 进程池中的进程通信

多任务文件夹复制

1 获取用户要copy的文件夹的名次
2 创建一个新的文件夹
3 获取文件夹的所有的待copy的文件名字
4 创建进程池
5 向进程池中添加拷贝任务

import multiprocessing
import os


def copy_file(q, file_name, new_folder_name, old_folder_name):
    """完成文件拷贝"""

    with open(old_folder_name + "/" + file_name, "rb") as f:
        content = f.read()

    # 保存到新的文件夹中
    new_file = open(new_folder_name + "/" + file_name, "wb")
    new_file.write(content)
    new_file.close()

    q.put(file_name)


def main():
    # 获取用户要复制的文件夹名字  test
    old_folder_name = input("请输入要复制的文件夹名字:")

    # 创建一个新的文件夹   test[复件]
    new_folder_name = old_folder_name + "复件"
    if not os.path.exists(new_folder_name):
        os.mkdir(new_folder_name)

    # 获取文件夹的所有待拷贝的文件名字
    file_names = os.listdir(old_folder_name)

    # 创建进程池
    po = multiprocessing.Pool(5)

    # 创建队列
    q = multiprocessing.Manager().Queue()

    # 添加拷贝任务
    for file_name in file_names:
        po.apply_async(copy_file, args=(q, file_name, new_folder_name, old_folder_name))

    po.close()

    po.join()


if __name__ == '__main__':
    main()

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值