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

1 篇文章 0 订阅
1 篇文章 0 订阅


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

多任务的介绍

同一时间执行多个任务

多任务的执行方式

  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我哦'
+v Lyj_txd
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

溏心蛋*

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值