Python -- 多任务、进程、线程

news2024/9/21 22:57:26

文章目录

  • 多任务的介绍
    • 多任务的执行方式
  • 进程
    • 进程的概念
    • 进程的作用
    • 进程的使用
      • 多进程
      • 获取进程编号
    • 进程执行带有参数的任务
      • 介绍
      • args参数和kwargs的使用
    • 进程注意点
      • 进程之间不共享全局变量
      • 主进程会等待所有子进程执行结束再结束
  • 线程
    • 介绍
    • 概念
  • 线程的作用
    • 多线程的使用
    • 线程执行有参数的任务
    • 线程注意点
      • 线程之间执行是无序的
      • 主线程会等待所有的子线程执行结束再
      • 线程之间共享全局变量
      • 线程之间共享变量数据出现错误问题
    • 互斥锁
      • 互斥锁的使用
      • 死锁
  • 进程和线程的对比
    • 关系对比
    • 区别对比
    • 优缺点

好久没写了,回归一下,不然被列为失踪人口了!!!👌

多任务的介绍

同一时间执行多个任务

多任务的执行方式

  1. 并发
    在一段时间内交替去执行任务。
  2. 并行
    对于多核cpu处理多任务,操作系统会给cpu的每个内核安排一个执行的软件,多个内核是真正的一起执行软件

进程

在Python程序中,想要实现多任务可以使用进程来完成,进程是实现多任务的一种方式。

进程的概念

一个正在运行 的程序或软件就是一个进程,它是操作系统进行资源分配的基本单位,也就是说每启动一个进程,操作系统都会给其分配一定的运行资源(内存资源)保证进程的运行。

  • 注意:
  • 应该程序至少有一个进程,一个进程默认有一个线程,进程里面可以创建多个线程,线程是依附在进程里面的,没有进程就没有线程

进程的作用

单进程:
默认程序运行创建一个进程:
单进程.py --> 进程{主线程[执行代码]}
多进程:
在这里插入图片描述

进程的使用

多进程

# 调用包
import multiprocessing
import time

# 跳舞任务
def dance():
    for i in range(3):
        print('跳舞中……')
        time.sleep(2)

# 唱歌任务
def sing():
    for i in range(3):
        print('唱歌中……')
        time.sleep(1)

# 创建子进程
dance_processing = multiprocessing.Process(target=dance)
sing_processing = multiprocessing.Process(target=sing)

# 启动对应的任务
if __name__ == '__main__':
    dance_processing.start()
    sing_processing.start()

获取进程编号

# 调用包
import multiprocessing
import time
import os

# 跳舞任务
def dance():
    # 获取当前进程(子进程)的编号
    dance_os = os.getpid()
    print('跳舞进程:', dance_os, multiprocessing.current_process())

    # 获取当前进程的父进程编号
    dance_os_parent = os.getppid()
    print('跳舞的父进程id:', dance_os_parent)

    for i in range(3):
        print(f'跳{i+1}舞中……')
        time.sleep(2)

# 唱歌任务
def sing():
    # 获取当前进程(子进程)的编号
    sing_os = os.getpid()
    print('唱歌进程:', sing_os, multiprocessing.current_process())

    # 获取当前进程的父进程编号
    sing_os_parent = os.getppid()
    print('唱歌的父进程id:', sing_os_parent)

    for i in range(3):
        print(f'唱{i+1}歌中……')
        time.sleep(1)
        os.kill(sing_os, 9)

if __name__ == '__main__':
    # 获取主进程id
    main_os = os.getpid()
    print('主进程:', main_os, multiprocessing.current_process())

    # 创建子进程
    dance_processing = multiprocessing.Process(target=dance, name='dance')
    # print('跳舞进程:', dance_processing)
    sing_processing = multiprocessing.Process(target=sing, name='sing')
    # print('唱歌进程:', sing_processing)

    # 启动对应的任务
    dance_processing.start()
    sing_processing.start()

进程执行带有参数的任务

介绍

Process类执行任务并给任务传参有两种方式:

  • args 表示以元祖的方式给执行任务传参
  • kwargs 表示以字典方式给执行任务传参

args参数和kwargs的使用

import multiprocessing
import time

# 跳舞任务
def dance(count):
    for i in range(count):
        print('跳舞中……')
        time.sleep(2)

# 唱歌任务
def sing(count):
    for i in range(count):
        print('唱歌中……')
        time.sleep(1)


# 启动对应的任务
if __name__ == '__main__':
    # 创建子进程
    dance_processing = multiprocessing.Process(target=dance, args=(3,))
    sing_processing = multiprocessing.Process(target=sing, kwargs={'count': 2})

    dance_processing.start()
    sing_processing.start()

进程注意点

  1. 进程之间不共享全局变量
  2. 主进程会等待所有的子进程结束再结束

进程之间不共享全局变量

import multiprocessing
import time

# 定义全局变量列表
g_list = []

# 添加数据的任务
def add_data():
    for i in range(3):
        # 不用加global(因为列表是可变类型,可以在原有内存的基础上修改数据,并且修改后内存地址不变)
        g_list.append(i)
        # 加上global 表示声明要修改全局变量的内存地址
        # g_list = [1, 2]
        print('append', i)
        time.sleep(0.2)
    print('添加完成:', g_list)

# 读取数据的任务
def read_data():
    print('read:', g_list)

if __name__ == '__main__':
    # 添加数据的子进程
    add_process = multiprocessing.Process(target=add_data)
    # 读取数据的子进程
    read_process = multiprocessing.Process(target=read_data)

    # 启动进程
    add_process.start()
    # 当前进程执行完成再执行完再继续往下执行
    add_process.join()
    read_process.start()
  • 结论: 进程之间不共享变量
    在这里插入图片描述

主进程会等待所有子进程执行结束再结束

根据以上代码测试,相信你已经发现了在这里插入图片描述

线程

介绍

在Python中,想要实现多任务除了使用进程以外,还可以使用线程来完成,线程是实现多任务的另一种方式。

概念

线程是进程中执行代码的一个分支,每一个执行分支(线程)想要执行代码需要cpu进行调度,也就是说线程是cpu调度的基本单位,每个进程至少都有一个线程,而这个线程就是我们通常说的主线程。

线程的作用

多线程可以完成多任务
多线程:
在这里插入图片描述
在这里插入图片描述

程序启动默认会有一个主线程,程序员自己创建的线程可以称为子线程,多线程可以完成多任务

多线程的使用

# 导入线程模块
import threading
import time


def dance():
    # 获取当前线程
    dance_thread = threading.current_thread()
    print('跳舞:', dance_thread)

    for i in range(3):
        print('跳舞ing')
        time.sleep(2)

def sing():
    # 获取当前线程
    current_thread = threading.current_thread()
    print('唱歌:', current_thread)

    for i in range(3):
        print('唱歌ing')
        time.sleep(1)


if __name__ == '__main__':
    # 获取当前线程
    main_thread = threading.current_thread()
    print('主线程:', main_thread)

    # 创建子线程
    dance_thread = threading.Thread(target=dance, name='dance')
    sing_thread = threading.Thread(target=sing, name='sing')

    # 启动子程序执行对于的任务
    dance_thread.start()
    sing_thread.start()

结果:

主线程: <_MainThread(MainThread, started 11968)>
跳舞: <Thread(dance, started 10584)>
跳舞ing
唱歌: <Thread(sing, started 3440)>
唱歌ing
唱歌ing
跳舞ing
唱歌ing
跳舞ing

线程执行有参数的任务

Thread类执行任务并给任务传参数有两种

  • args 表示以元祖的方式给执行任务传参
  • kwargs 表示以字典方式给执行任务传参
# 导入线程模块
import threading
import time


def dance(count):
    # 获取当前线程
    dance_thread = threading.current_thread()
    print('跳舞:', dance_thread)

    for i in range(count):
        print('跳舞ing')
        time.sleep(2)

def sing(count):
    # 获取当前线程
    current_thread = threading.current_thread()
    print('唱歌:', current_thread)

    for i in range(count):
        print('唱歌ing')
        time.sleep(1)


if __name__ == '__main__':
    # 获取当前线程
    main_thread = threading.current_thread()
    print('主线程:', main_thread)

    # 创建子线程
    dance_thread = threading.Thread(target=dance, name='dance', args=(2,))
    sing_thread = threading.Thread(target=sing, name='sing', kwargs={'count': 3})

    # 启动子程序执行对于的任务
    dance_thread.start()
    sing_thread.start()

结果:

主线程: <_MainThread(MainThread, started 7412)>
跳舞: <Thread(dance, started 15816)>
跳舞ing
唱歌: <Thread(sing, started 18212)>
唱歌ing
唱歌ing
跳舞ing
唱歌ing

线程注意点

  1. 线程之间是无序的
  2. 主线程会等待所有的子线程结束再结束
  3. 线程之间共享全局变量
  4. 线程之间共享全局变量数据出现错误问题

线程之间执行是无序的

import threading
import time

def task():
    time.sleep(1)
    # 获取当前线程
    print(threading.current_thread())

if __name__ == '__main__':
    # 循环创建大量线程,测试线程之间执行是否无序
    for i in range(20):
        # 每循环一次创建一个子线程
        sub_thread = threading.Thread(target=task)
        # 启动子线程对应的任务
        sub_thread.start()

结果:

<Thread(Thread-4 (task), started 16424)><Thread(Thread-11 (task), started 16816)><Thread(Thread-5 (task), started 11396)>
<Thread(Thread-17 (task), started 14800)>
<Thread(Thread-13 (task), started 23500)><Thread(Thread-20 (task), started 28080)>
<Thread(Thread-9 (task), started 32000)>
<Thread(Thread-6 (task), started 15764)><Thread(Thread-19 (task), started 5112)>
<Thread(Thread-1 (task), started 33744)>
<Thread(Thread-2 (task), started 26248)>
<Thread(Thread-18 (task), started 19632)><Thread(Thread-15 (task), started 20116)>
<Thread(Thread-14 (task), started 24540)><Thread(Thread-10 (task), started 28732)>
<Thread(Thread-12 (task), started 10912)>
<Thread(Thread-16 (task), started 25176)><Thread(Thread-8 (task), started 14792)>





<Thread(Thread-7 (task), started 20020)>


<Thread(Thread-3 (task), started 28072)>

线程之间执行是无序的,具体那个线程执行是由cpu调度决定的

主线程会等待所有的子线程执行结束再

import threading
import time

def task():
    while True:
        print('任务执行中……')
        time.sleep(0.3)

if __name__ == '__main__':
    # 创建子进程
    task_thread = threading.Thread(target=task)
    # 启动子线程对应的任务
    task_thread.start()

    #  主进程延迟1s
    time.sleep(1)
    print('over')

结果:

任务执行中……
任务执行中……
任务执行中……
任务执行中……
over
任务执行中……
任务执行中……
任务执行中……
任务执行中……

......

线程之间共享全局变量

import threading

# 定义全局变量
g_list = []

# 添加数据的任务
def add_data():
    for i in range(1, 4):
        g_list.append(i)
        print('add:', i)
    print('添加完成')

# 创建读取的任务
def read_data():
    print('g_list:', g_list)


if __name__ == '__main__':
    add_thread = threading.Thread(target=add_data)
    read_thread = threading.Thread(target=read_data)

    add_thread.start()
    add_thread.join()
    read_thread.start()

结果:

add: 1
add: 2
add: 3
添加完成
g_list: [1, 2, 3]

线程之间共享变量数据出现错误问题

import threading

g_num = 0

def task1():
    for i in range(10000000):
        global g_num
        g_num += 1
    print('task1', g_num)

def task2():
    for i in range(10000000):
        global g_num
        g_num += 1
    print('task2', g_num)


if __name__ == '__main__':
    first_thread = threading.Thread(target=task1)
    second_thread = threading.Thread(target=task2)

    first_thread.start()
    second_thread.start()

总数输出可能比200w少

解决方法:
线程同步:保证同一时刻只能有一个线程去操作全局变量,同步就是协同步调,按预定的先后次序进行运行。
线程同步的方式:

  1. 线程等待(join)
  2. 互斥锁

互斥锁

对共享数据进行锁定,保证同一时刻只能有一个线程去操作

注意:
互斥锁是多个线程一起去抢,抢到锁的线程先执行,没抢到的线程需要等待,等互斥锁使用完释放后,其他等待的线程再去抢这个锁。

互斥锁的使用

import threading

g_num = 0

# 创建互斥锁,Lock本质是一个函数,通过调用函数可以创建一个互斥锁
lock = threading.Lock()

def task1():
    # 上锁
    lock.acquire()
    for i in range(10000000):
        global g_num
        g_num += 1
    print('task1', g_num)
    # 释放锁
    lock.release()

def task2():
    # 上锁
    lock.acquire()
    for i in range(10000000):
        global g_num
        g_num += 1
    print('task2', g_num)
    # 释放锁
    lock.release()


if __name__ == '__main__':
    first_thread = threading.Thread(target=task1)
    second_thread = threading.Thread(target=task2)

    first_thread.start()
    second_thread.start()

结果

task1 10000000
task2 20000000

总结:线程等待和互斥锁都是把多任务改成单任务去执行,保证了数据的准确性,但是执行性能会下降

死锁

一直等待对方释放锁的情景就是死锁

进程和线程的对比

  1. 关系对比
  2. 区别对比
  3. 优缺点对比

关系对比

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

区别对比

  1. 进程之间不共享全局变量
  2. 线程之间共享全局变量,但是要注意资源竞争的问题,解决办法:互斥锁或者线程同步
  3. 创建进程的资源开销要比创建线程资源开销大
  4. 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
  5. 线程不能够独立执行,必须依存在进程中
  6. 多进程开发比单进程/多线程开发稳定性要强

优缺点

  • 进程:
    • 优点:可以用多核
    • 缺点:资源开销大

用于计算密集型的相关操作使用多进程

  • 线程:
    • 优点:资源开销小
    • 缺点:不能使用多核

文件写入,文件的下载,I/O操作

'有爬虫需求的dd我哦'
lu_txd

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/740187.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在OpenCV中进行图像预处理

今天&#xff0c;我们进一步深入&#xff0c;并处理在图像处理中常用的形态学操作。形态学操作用于提取区域、边缘、形状等。 什么是形态学操作&#xff1f; 形态学操作是在二值图像上进行的。二值图像可能包含许多不完美之处。特别是由一些简单的阈值操作产生的二值图像&#…

2023-07-10 cmake管理的项目中使用vcpkg管理第三方库

一、安装 从Github上克隆Vcpkg仓库然后执行安装命令即可&#xff1a; git clone https://github.com/microsoft/vcpkg .\vcpkg\bootstrap-vcpkg.bat 安装自己需要的第三方库 .\vcpkg\vcpkg install [packages to install] 更多教学可参考&#xff1a; https://learn.microsoft…

kubenetes手动安装V1.22.4

kubenetes手动安装V1.22.4 1、主节点和工作节点需要的组件 提示&#xff1a;为了方便测试请关闭selinux、关闭防火墙、swap. SELinux防火墙的设置&#xff1a; [rootlocalhost]#getenforce Disabled # 修改/etc/selinux/config文件 vim /etc/selinux/config 将SELINUXenfor…

【python】Excel文件的读取操作

测试用例.xlsx文件内容 利用xlrd模块读取文件 import xlrdfilename 测试用例.xlsx # 读取Excel表文件 wb xlrd.open_workbook(filename) # 读取Excel表中的第一个工作表 sheet wb.sheet_by_index(0) # 读取有内容的表格行数 rows sheet.nrows # 读取有内容的表格列数 cols…

嵌入式系统之ADC采样

嵌入式系统往往会有模拟信号的采集&#xff0c;比如模拟传感器温度、压力、流量、速度、光强等模拟量&#xff0c;经过放大整形滤波电路后送给ADC芯片&#xff0c;将电信号转转变成离散的数字量这个过程称之为AD采样&#xff0c;AD采样应用广泛&#xff0c;普遍遵循采样率3倍于…

ES 知识体系

https://www.easyice.cn/archives/367

【力扣算法03】之正则表达式匹配- python

文章目录 问题描述示例 1示例2示例3提示 思路分析代码分析完整代码运行效果及示例代码示例代码1运行结果示例代码2运行结果示例代码3运行结果 完结 问题描述 给你一个字符串 s 和一个字符规律 p&#xff0c;请你来实现一个支持 ‘.’ 和 ‘’ 的正则表达式匹配。 ‘.’ 匹配任意…

企业省时又省力:人工智能电话客服机器人的广泛应用

人工智能技术的崛起正深刻地改变着各个行业&#xff0c;其中之一便是客服领域。过去&#xff0c;人们常常在电话中与生硬的自动语音应答机打交道&#xff0c;这种体验常常令人沮丧。然而&#xff0c;随着人工智能电话客服机器人的广泛应用&#xff0c;企业不仅能够省时又省力&a…

MYSQL数据库系统期末试题及参考答案(2)

期末试题 : 一&#xff0c;创建数据库Game 二&#xff0c;数据表操作 1、创建表格players&#xff0c;记录游戏玩家信息&#xff1a; player_id&#xff1a;玩家ID&#xff0c;主键 player_name&#xff1a;玩家姓名&#xff0c;不能为空 age&#xff1a;年龄&#xff0c;必须…

Python数据分析常见Matplotlib SeaBorn图表

Matplotlib绘图 Matplotlib基本概念 Matplotlib&#xff1a;基于对象的思维构建的视觉符号。 每一个Axes&#xff08;坐标轴&#xff09;对象包含一个或者多个Axis(轴)对象&#xff0c;比如X轴、Y轴。 一个Figure&#xff08;画像&#xff09;是由一堆坐标轴对象组成的。 换…

2023中国数交会|美创科技获数字和软件服务行业两项大奖!

7月6日-9日&#xff0c;为期四天的2023中国国际数字和软件服务交易会&#xff08;简称&#xff1a;数交会&#xff09;圆满落幕。 作为国务院批准举办的国家级展会&#xff0c;本届数交会由商务部、科技部、中国贸促会和辽宁省政府主办&#xff0c;以“数字创新、融合发展”为主…

操作系统实战45讲|03.黑盒之中有什么、04.震撼的Linux全景图

03.黑盒之中有什么 黑盒之中有什么 从抽象的角度来看&#xff0c;内核就是计算机资源的管理者&#xff0c;管理资源是为了让应用使用资源。 计算机中的资源分为两类&#xff1a;硬件资源、软件资源&#xff1b; 硬件资源有以下这些&#xff1a; 总线&#xff0c;负责连接各种…

一个Transformer在尺度上适合多模态扩散的所有分布

文章目录 One Transformer Fits All Distributions in Multi-Modal Diffusion at Scale摘要本文方法实验结果 One Transformer Fits All Distributions in Multi-Modal Diffusion at Scale 摘要 本文提出了一个统一的扩散框架(UniDiffuser)来拟合一个模型中与一组多模态数据相…

Vue3+Vite+Pinia+Naive后台管理系统搭建之三:vue-router 的安装和使用

前言 如果对 vue3 的语法不熟悉的&#xff0c;可以移步 Vue3.0 基础入门快速入门。 如果对 vue-router 语法不熟悉的&#xff0c;可以移步Vue3 系列&#xff1a;vue-router。 1. 安装依赖 yarn add vue-router // or npm install vue-router 2. 构建 src/router/index.js …

对话式ai人工智能的主要好处有哪些

对话式 AI 是客户服务的一个重要且不断增长的组成部分&#xff0c;尤其是客户越来越多地采用的数字自助服务。 对话式 AI 可以在提高客户满意度 (CSAT) 方面发挥很重要的作用。在 IBM 于 2021 年进行的一项研究中&#xff0c;99% 的公司报告称&#xff0c;由于使用虚拟对话式 …

支持源码的低代码核心工具,逻辑引擎

在现代企业管理中&#xff0c;决策扮演着至关重要的角色。然而&#xff0c;随着业务规模的扩大和数据量的增加&#xff0c;人工决策变得越来越困难和耗时&#xff0c;而且容易受到主观因素的影响。逻辑引擎的出现为企业提供了一种高效、准确的决策推理工具&#xff0c;能够以逻…

数字化转型迫在眉睫

在挑战商业世界现状并实现数字化转型时&#xff0c;一定程度的阻力是不可避免的。事实上&#xff0c;《福布斯》的一篇文章援引哈佛商学院的研究表明&#xff0c;70%的组织变革努力都失败了&#xff0c;“原因之一是高管们没有从足够多的人那里了解他们的计划和想法。”支持。”…

市面上的ipad国产触控笔怎么样?好用的电容笔合集

而对那些把IPAD当作学习工具的人而言&#xff0c;这个Apple Pencil绝对是不可或缺的。然而&#xff0c;苹果版本的Pencil却是昂贵得让许多人望而却步。因此&#xff0c;最佳方法是选择一个平替的电容笔。我是从几年前开始用IPAD的&#xff0c;也是一个数码爱好者&#xff0c;近…

AIGC行业周刊【2023-0709】【第六期】2023年世界人工智能大会大佬发言汇总

点击加入->【智子纪元-AIGC】技术交流群 一、大咖观点&#xff1a; 0709AI日报&#xff1a;2023年世界人工智能大会大佬发言汇总「5年内&#xff0c;人类程序员没了」&#xff0c;Stability AI老板大胆预测&#xff0c;一众大佬狂怼&#xff1a;大错特错&#xff0c;都懒得…

在vite创建的vue3项目中加载Cesium立体地形信息并调整初始化角度

在vite创建的vue3项目中加载Cesium立体地形信息并调整初始化角度 使用vite创建vue3项目 npm create vitelatestcd到创建的项目文件夹中 npm install安装Cesium npm i cesium vite-plugin-cesium vite -D配置 &#xff08;1&#xff09;在项目的vite.config.js文件中添加&#x…