吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1402|回复: 12
收起左侧

[学习记录] Python多线程编程

  [复制链接]
daokunn 发表于 2022-11-10 13:30

文章根据网课整理的笔记,如有错误,请不吝赐教!

进程

进程介绍

进程的概念:进程(Process)是资源分配的最小单位,它是操作系统进行资源分配和调度运行的基本单位,是动态的。例如正在运行的QQ,浏览器等。

一个程序运行后至少有一个进程。

那么,多进程有什么用呢?先看到一下程序。

def fun_A():
    print('任务A')
def fun_B():
    print('任务B')

fun_A()
fun_B()

运行这个程序,会按照代码的执行顺序,fun_A函数执行完毕以后才会执行fun_B函数。如果我们让fun_A和fun_B同时运行,那么这个效率显然会大大提升。而这也就是多进程。

进程的创建

  1. 导入进程包

    import multiprocessing

  2. 通过进程类创建进程对象

    进程对象 = multiprocessing.Process(target = 任务名)

    • taget : 这里一般是指函数名,方法名
    • name:进程名,一般不用设置
    • group:进程组,目前只能使用None
  3. 启动进程执行任务

    进程对象.start()

import time
import multiprocessing  # 第一步,导入进程包
# 唱歌函数
def sing():
    for i in range(3):
        print('唱歌')
        time.sleep(0.5) # 设置延迟
# 跳舞函数
def dance():
    for i in range(3):
        print('跳舞')
        time.sleep(0.5)
if __name__ == '__main__':
    # 使用进程类创建进程对象
    sing_process = multiprocessing.Process(target=sing)
    dance_process = multiprocessing.Process(target=dance)
    # 启动
    sing_process.start()
    dance_process.start()

带参数的进程创建

传参有两种方式:

  • args: 以元组的方式传入,顺序一一对应,只有一个参数注意逗号(参数1,)

  • kwargs: 以字典的方式传入,顺序随意,但是key要和函数的参数名字一样

    import time
    import multiprocessing  
    def sing():
      for i in range(3):
          print('唱歌')
          time.sleep(0.5) 
    def dance():
      for i in range(3):
          print('跳舞')
          time.sleep(0.5)
    
    if __name__ == '__main__':
      # 使用进程类创建进程对象
      sing_process = multiprocessing.Process(target=sing)
      dance_process = multiprocessing.Process(target=dance)
      # 启动
      sing_process.start()
      dance_process.start()
    

获取进程的编号

但程序越来越多时,不好区分进程的之间的关系,为了方便管理,我们需要获取进程编号。

有两种方式:

  • 获取当前进程编号

    os.getpid()

  • 获取当前父进程的编号

    os.getppid()

通俗的说,谁启动就是父进程,这里"main"是主进程,启动了两个子进程,sing_process和dance_process。

import time
import os
import multiprocessing  # 第一步,导入进程包
# 唱歌函数
def sing(num):
    print('我是唱歌进程:',os.getpid())
    print('唱歌的父进程:', os.getppid())
    for i in range(num):
        print('唱歌')
        time.sleep(0.5) # 设置延迟
# 跳舞函数
def dance(num):
    print('我是跳舞进程:', os.getpid())
    print('跳舞的父进程:', os.getppid())
    for i in range(num):
        print('跳舞')
        time.sleep(0.5)
if __name__ == '__main__':
    print('我是父进程:', os.getpid())
    sing_process = multiprocessing.Process(target=sing,args=(2,))
    dance_process = multiprocessing.Process(target=dance,kwargs={'num':1})
    sing_process.start()
    dance_process.start()

进程注意点:

  1. 主进程会等待所有子进程执行结束再结束
  2. 设置守护子进程 子进程对象.daemon = True,主进程退出后子进程直接销毁。

案列:文件夹高并发copy器

线程

线程介绍

线程:线程是程序执行的最小单位。同一进程下的线程可以共享进程所拥有的全部资源。(进程是包工头,负责领取资源;线程是工人,负责干活)

线程创建对象

  1. 导入线程模块

    import threading

    1. 通过线程类创建线程对象

    threading.Thread(target=任务名)

    1. 启动线程执行任务

    线程对象.start()

传参和进程是一样的,一并演示。

demo如下:

import time
import os
import threading
def sing(num):
    for i in range(num):
        print('唱歌')
        time.sleep(0.5)

def dance(num):
    for i in range(num):
        print('跳舞')
        time.sleep(0.5)
if __name__ == '__main__':
    sing_thread = threading.Thread(target=sing,args=(5,))
    dance_thread = threading.Thread(target=dance,kwargs={'num':4})
    sing_thread.start()
    dance_thread.start()

主线程和子线程的结束顺序

主线程会等待所有的子线程结束在结束

demo如下:

import time
import threading

def work():
    for i in range(10):
        print('工作....')
        time.sleep(0.2)

if __name__ == '__main__':
    sub_thread = threading.Thread(target=work)
    sub_thread.start()

    time.sleep(1)
    print("主线程结束了")  # 主线程其实没有结束,还在等待着子线程

设置守护主线程

  • 创建时设置,如sub_thread = threading.Thread(target=work),daemon=True

    import time
    import threading
    
    def work():
      for i in range(10):
          print('工作....')
          time.sleep(0.2)
    
    if __name__ == '__main__':
      sub_thread = threading.Thread(target=work,daemon=True)
      sub_thread.start()
    
      time.sleep(1)
      print("主线程结束了")  # 因为子线程守护主线程,所以主线程和子线程这时候都乖乖结束
  • 使用`线程对象.setDeamon(True)

    import time
    import threading
    
    def work():
      for i in range(10):
          print('工作....')
          time.sleep(0.2)
    
    if __name__ == '__main__':
      sub_thread = threading.Thread(target=work)
      sub_thread.setDaemon(True)
      sub_thread.start()
    
      time.sleep(1)
      print("主线程结束了")  

线程的执行顺序

线程之间的执行的顺序是无序的

获取当前的线程信息

# 通过current_thread方法获取线程对象
current_thread = thread.current_thread()
# 通过current_thread对象可以知道线程的相关信息,例如创建顺序
print(current_thread)

进程和线程的对比

  1. 关系对比

    1. 线程是依附在进程里面的,没有进程就没有线程
    2. 一个进程默认提供一条线程,进程可以创建多个线程
  2. 区别

    1. 创建进程的资源开销要比创建线程的资源开销大
    2. 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
    3. 线程不能独立运行
  3. 优缺点对比

    1. 进程优缺点
      • 优点:可以用多核
      • 缺点:资源开销大
    2. 线程优缺点
      • 优点:资源开销小
      • 不能使用多核

案例:高并发cpoy器

多进程高并发cpoy器

  1. 定义源文件夹所在的路径,目标文件夹所在的路径

    source_dir = ""
    dest_dir = ""
  2. 创建目标文件夹

    try:
       os.mkdir(dest_dir)
    except:
       print('目录已存在!')
  3. 通过os.listdir获取源目录的文件列表

    file_list = os.listdir(source_dir)
    # print(file_list)
  4. 遍历每个文件,定义一个函数,专门实现文件拷贝

    for file_name in file_list:
        copy_work(file_name,source_dir,dest_dir)
  5. 对4进行优化,采用进程多任务,完成高并发拷贝

    for file_name in file_list:
       copy_work(file_name,source_dir,dest_dir)
       sub_process = multiprocessing.Process(target=copy_wrok,args=(file_name,source_dir,dest_dir))
       sub_process.start()

最终代码

import multiprocessing
import os

def copy_work(file_name,source_dir,dest_dir):
    # 拼接文件路径
    source_path = source_dir + "/" + file_name
    dest_path = dest_dir + "/" + file_name

    # 打开源文件和目标文件
    with open(source_path,"rb") as source_file:
        with open(dest_path,"wb" ) as dest_file:
          # 循环读取源文件到目标路径
            while True:
                data = source_file.read()
                if data:
                    dest_file.write(data)
                else:
                    break

if __name__ == "__main__":
    # 定义源文件夹和目标文件夹
    source_dir = "D:/test1"
    dest_dir = "D:/test2"

    # 创建目标文件夹
    try:
        os.mkdir(dest_dir)
    except:
        print('目录已存在!')

    # 读取源文件夹的文件列表
    file_list = os.listdir(source_dir)
    print(file_list)

    # 遍历文件列表实现拷贝
    for file_name in file_list:
        copy_work(file_name, source_dir, dest_dir)
        sub_process = multiprocessing.Process(target=copy_work, args=(file_name, source_dir, dest_dir))
        sub_process.start()

免费评分

参与人数 4吾爱币 +3 热心值 +3 收起 理由
obitosec + 1 我很赞同!
zz443470785 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
zz08808 + 1 + 1 热心回复!
zhaoqingdz + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

笨蛋先森 发表于 2022-11-10 14:45
先收藏,后面看不看到时候再说
qwqwqw9999 发表于 2022-11-10 14:54
stone0505 发表于 2022-11-10 15:21
黑泽心教 发表于 2022-11-10 15:33
学习了 感谢😊
wln521 发表于 2022-11-10 15:39
66666
head_yzb 发表于 2022-11-10 15:43
感谢大佬分享,学习ing!
ManaCola 发表于 2022-11-10 15:58
码住,肯定是后面python学习必不可少的一步

wkdxz 发表于 2022-11-10 16:38
本帖最后由 wkdxz 于 2022-11-10 16:46 编辑

[Asm] 纯文本查看 复制代码
# 遍历文件列表实现拷贝
for file_name in file_list:
    
    #普通拷贝,方法1
    copy_work(file_name, source_dir, dest_dir)
    
    
    #加速拷贝,方法2
    sub_process = multiprocessing.Process(target=copy_work, args=(file_name, source_dir, dest_dir))
    sub_process.start()


示例代码好像多执行了一次,我在电脑上测试了一下:方法1还快些

文件比较杂乱,大小从10K到12M都有
Snipaste_2022-11-10_16-29-59.jpg
余律师 发表于 2022-11-10 16:55
感谢楼主分享!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-25 07:03

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表