进程&多任务文件夹复制

87 阅读5分钟

进程和程序

进程:正在执行的程序

程序:没有执行的代码,是一个静态的

进程的状态

图片.png

使用进程实现多任务

multiprocessing模块就是跨平台的多进程模块,提供了一个Process类来代表一个进程对象,这个对象可以理解为是一个独立的进程,可以执行另外的事

# -*- coding: UTF-8 -*-
'''
@Project :网络爬虫 
@File    :1-进程实现.py
@IDE     :PyCharm 
@Author  :慕逸
@Date    :10/11/2024 21:58 
'''

import multiprocessing
import threading
import time

def demo1():
    while True:
        print("--1--")
        time.sleep(1)


def demo2():
    while True:
        print("--2--")
        time.sleep(1)


def main():
    # t1 = threading.Thread(target=demo1)
    # t2 = threading.Thread(target=demo2)
    # t1.start()
    # t2.start()
    p1 = multiprocessing.Process(target=demo1)
    p2 = multiprocessing.Process(target=demo2)
    p1.start()
    p2.start()



if __name__ == '__main__':
    main()

线程和进程之间的对比

进程: 能够完成多任务,eg: 一台电脑上可以同时运行多个QQ

线程: 能够完成多任务,eg:一个QQ中的多个聊天窗口

根本区别:

进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位

线程依赖进程存在

进程间通信--Queue

图片.png

# -*- coding: UTF-8 -*-  
'''  
@Project :网络爬虫  
@File :2-队列.py  
@IDE :PyCharm  
@Author :慕逸  
@Date :10/11/2024 22:29  
'''  
  
from multiprocessing import Queue  
  
q = Queue(3)  
  
# raise "Queue is full!!!"  
q.put(1)  
q.put(2)  
q.put(3)  
# q.put(4) # 阻塞  
# q.put_nowait(4) # 不阻塞  
  
# 取值  
print(q.get())  
print(q.get())  
print(q.get())

线程间简单通信

模拟下载数据,与数据处理

图片.png

import multiprocessing
from multiprocessing import Queue


def download(q):
    """
    下载数据,放入队列中
    :return: None
    """
    lis = [11, 22, 33, 44]
    for item in lis:
        q.put(item)

    print("数据下载完成,并且保存到队列中...")


def parse_data(q):
    """
    数据处理
    :return:
    """
    data = list()
    while True:
        q_get_data = q.get()
        data.append(q_get_data)

        if q.empty():
            break

    print(data)


def main():
    q = Queue(3)

    t1 = multiprocessing.Process(target=download, args=(q,))
    t2 = multiprocessing.Process(target=parse_data, args=(q,))
    t1.start()
    t2.start()


if __name__ == '__main__':
    main()

图片.png

进程共享全局变量

在多进程环境中,不同进程通常拥有各自独立的内存空间,这意味着一个进程中定义的全局变量默认不会被其他进程直接访问。然而,在某些情况下,我们可能希望让多个进程能够共享某些数据。为了实现这一点,操作系统提供了多种机制来支持进程间通信(IPC, Inter-Process Communication)以及数据共享。

以下是一些常见的方法来实现进程间的全局变量共享:

  1. 共享内存

    • 共享内存是一种允许两个或更多进程共享同一块物理内存区域的技术。这是最快的一种IPC方式,因为它避免了数据的复制。
  2. 命名管道(FIFOs)

    • 命名管道是一种单向或双向的通信通道,它允许进程之间通过读写管道来传递数据。虽然主要用于消息传递,但也可以用于传递结构化的数据,从而间接实现数据共享。
  3. 消息队列

    • 消息队列允许进程异步地发送和接收包含数据结构的消息。这种方式适合于需要可靠的数据传输场景。
  4. 信号量

    • 信号量不是用来直接共享数据的,但它可以与共享内存一起使用,以确保当一个进程正在修改共享内存中的数据时,其他进程不能同时访问这块内存,以此来保证数据的一致性和完整性。
  5. 套接字

    • 套接字可用于不同机器上的进程间通信,同样适用于同一台机器上的进程。它们提供了一种通用的方法来交换数据,并且可以基于TCP/IP协议进行网络通信。
  6. 文件

    • 进程可以通过读写同一个文件来共享信息。这种方法简单但是效率较低,因为它涉及到磁盘I/O操作。
  7. 内存映射文件

    • 内存映射文件将文件的一部分或全部映射到内存中,这样不同的进程就可以通过映射同一文件的不同部分来进行数据共享。

但是,共享全局变量不适用于多进程编程


a = 1


def demo1():
    global a
    a += 1


def demo2():
    print(a) # 1 进程之间的全局变量不共享


def main():
    t1 = multiprocessing.Process(target=demo1)
    t2 = multiprocessing.Process(target=demo2)
    t1.start()
    t2.start()


if __name__ == '__main__':
    main()

进程池

当需要创建的⼦进程数量不多时,可以直接利⽤multiprocessing中的Process动态⽣成多个进程,但是如果是上百甚⾄上千个⽬标,⼿动的去创建的进程的⼯作量巨⼤,此时就可以⽤到multiprocessing模块提供的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)  # 随机睡眠0~2秒
    t_stop = time.time()

    print(msg, "执行完成,耗时%0.2f" % (t_stop - t_start))


if __name__ == '__main__':
    po = Pool(3)  # 创建进程池,指定进程池中的进程数量
    for i in range(0, 10):
        po.apply_async(worker, (i,))

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

    po.join()  # 等待po中所有子进程执行完成,必须放在close语句之后
    print("--end--")

图片.png

多任务文件夹复制

1 获取⽤户要copy的⽂件夹的名次

2 创建⼀个新的⽂件夹

3 获取⽂件夹的所有的待copy的⽂件名字

4 创建进程池

5 向进程池中添加拷⻉任务

import multiprocessing
from multiprocessing import Pool
import os
import time


def copy_file(q,old_folder_name, new_folder_name, file_name):
    """
    文件拷贝
    :return:
    """
    time.sleep(0.5)
    # print("\r从[%s]到[%s]拷贝的文件名称为[%s]" % (old_folder_name, new_folder_name, file_name), end='')

    old_file = open(old_folder_name + '/' + file_name, 'rb')
    content = old_file.read()
    old_file.close()

    new_file = open(new_folder_name + '/' + file_name, 'wb')
    new_file.write(content)
    new_file.close()

    q.put(file_name)


def main():
    # 获取要复制的文件夹名字
    old_folder_name = input("请输入待复制的文件夹名字: ")
    # os.path.exists(old_folder_name) # 判断文件夹是否存在

    # 创建一个新的文件夹
    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)
    # print(file_names)
    # 创建进程池
    po = Pool(5)
    q = multiprocessing.Manager().Queue()
    # 向进程池添加任务
    for file_name in file_names:
        po.apply_async(copy_file, args=(q,old_folder_name, new_folder_name, file_name))

    po.close()
    # po.join()

    # 复制进度条
    copy_file_num = 0
    file_count = len(file_names)
    while True:
        file_name = q.get()
        copy_file_num += 1
        print("\r拷贝进度 %.2f %%" % (copy_file_num * 100 / file_count), end='')

        if copy_file_num == file_count:
            break


if __name__ == '__main__':
    main()