进程和线程

进程

多任务

并发:时间片轮转,第一个进程干0.几毫秒,紧接着轮到第二个,这样转

并行:如果是多核cpu,每个cpu干每个的事情,一起执行

image-20240916154125331

在python中怎么实现多任务

进程和线程还有协程

进程的概念

image-20240916154506705

进程就是问操作系统索要内存空间,真正干活的是线程

进程的作用

image-20240916154648134

image-20240916154716807

image-20240916154759298

小结

  • 进程是操作系统进行资源分配的单位
  • 进程是python程序中实现多任务的一种方式

多进程的使用(python)

导入进程包

import multiprocessing

Process进程类的说明

image-20240916155154045

玩一玩:

# 1.导入包
import multiprocessing
import time


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

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

# 2.创建子进程(自己动手创建的进程为子进程,在__init__.py已经导入了Process类)
# 1》group:进程组 目前只能使用None,一般不需要设置
# 2》target: 进程执行的目标任务
# 3》name: 进程名,如果不设置,默认是Process-1,。。。以此类推
dance_process = multiprocessing.Process(target=dance)

# 3.启动进程执行时对应的任务
dance_process.start()

# 主进程执行唱歌任务
sing()
# 1.导入包
import multiprocessing
import time


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

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

# 2.创建子进程(自己动手创建的进程为子进程,在__init__.py已经导入了Process类)
# 1》group:进程组 目前只能使用None,一般不需要设置
# 2》target: 进程执行的目标任务
# 3》name: 进程名,如果不设置,默认是Process-1,。。。以此类推
dance_process = multiprocessing.Process(target=dance)
sing_process = multiprocessing.Process(target=sing)

# 3.启动进程执行时对应的任务
dance_process.start()
sing_process.start()

# 进程执行是无序的,具体哪个进程先执行是由操作系统调度决定的
dance_process.join()

获取进程编号

能够知道如果获取进程编号

获取进程编号的目的

image-20240916161735531

获取当前进程编号

os.getpid() 表示获取当前进程编号

import multiprocessing
import signal
import time
import os

# 跳舞任务
def dance():
    # 获取当前进程(子进程)的编号
    dance_process_id = os.getpid()
    print("dance_process_id:", dance_process_id, multiprocessing.current_process())
    # 获取当前进程父进程的编号
    dance_process_parent_id = os.getppid()
    print("dance_process的父进程编号是::", dance_process_parent_id)

    for i in range(3):
        print("跳舞中...")
        time.sleep(0.2)
        # 扩展:根据进程号,利用kill去杀死进程
        os.kill(dance_process_id, 9)

# 唱歌任务
def sing():
    sing_process_id = os.getpid()
    print("sing_process_id:", sing_process_id, multiprocessing.current_process())
    sing_process_parent_id = os.getppid()
    print("sing_process的父进程编号是:", sing_process_parent_id)
    for i in range(3):
        print("唱歌中...")
        time.sleep(0.2)

# 获取当前进程(主进程)的编号
main_process_id = os.getpid()
# 获取当前进程对象,查看当前代码是由哪个进程执行的
print("main_process_id:", main_process_id,multiprocessing.current_process())

# 如果不是主文件也需要这个判断
if __name__ == '__main__':
    # 创建子进程
    dance_process = multiprocessing.Process(target=dance,name='dance_process')
    print("dance_process:", dance_process)
    sing_process = multiprocessing.Process(target=sing,name='sing_process')
    print("sing_process:", sing_process)
    # 启动进程执行对应的任务
    dance_process.start()
    sing_process.start()

    # 等待子进程完成
    dance_process.join()
    sing_process.join()

进程执行带有参数的任务

  • 能够写出进程执行带有参数的任务

进程执行带有参数的任务的介绍

image-20240916233823197

args参数的使用

import multiprocessing

def show_info(name, age):
    print(name, age)

def show_info_2(name, age):
    print(name, age)

if __name__ == '__main__':
    # 创建子进程
    # 以元组的方式传参
    sub_process = multiprocessing.Process(target=show_info, args=("高菲", 20))

    # 以字典的方式传参
    sub_process_2 = multiprocessing.Process(target=show_info_2, kwargs={"age": 20,"name": "菲阿"})
    # 启动进程
    sub_process.start()
    sub_process_2.start()
    # 等待子进程结束
    sub_process.join()
    sub_process_2.join()

进程的注意点

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

进程间不共享全局变量

import multiprocessing
import time
# 定义全局变量

g_list = list()

#添加数据
def add_data():
    for i in range(3):
        # 列表是可变类型,可以在原有内存的基础上修改数据,并且修改后的内存地址不变
        # 所以不需要加上global关键字
        # 加上global 表示声明要修改全局变量的内存地址
        g_list.append(i)
        print("add",i)
        time.sleep(0.2)
    print("添加完成: ",g_list)

# 读取数据
def read_data():
    print("read",g_list)

# 添加数据子进程
add_process = multiprocessing.Process(target=add_data)

# 读取数据子进程
read_process = multiprocessing.Process(target=read_data)

# 启动进程,执行对应的任务
add_process.start()
# 当前进程等待添加数据的进程执行完成一会代码再继续往下执行
add_process.join()
print("main:",g_list)
read_process.start()

# 结论: 进程之间不共享全局变量

image-20240917114648308

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

import multiprocessing
import time

def task():
    for i in range(10):
        print("任务执行中...")
        time.sleep(0.2)

# 判断是否为主模块,程序入口模块
if __name__ == '__main__':
    # 创建子进程
    sub_process = multiprocessing.Process(target=task)
    # 把子进程设置为守护进程,以后主进程退出子进程之间销毁
    sub_process.daemon = True
    sub_process.start()

    # 主进程延时0.5s
    time.sleep(0.5)
    # 退出主进程之前先让子进程销毁
    sub_process.terminate()
    print("over")

# 结论:主进程会等待子进程执行完了再执行

两种方式可以取消:

 # 退出主进程之前先让子进程销毁
    sub_process.terminate()
    
# 把子进程设置为守护进程,以后主进程退出子进程之间销毁
    sub_process.daemon = True

线程

线程是什么

image-20240917151132300

线程的作用

多线程可以完成多任务

多线程是可以共享全局变量的

image-20240917151304462

线程是python程序中实现多任务的另一种方式,线程的执行需要cpu的调度

多线程的使用

导入线程模块

import threading

线程类Thread参数说明

image-20240917151622407

启动线程

启动线程用strat方法

多线程完成多任务的代码

# 1.导入线程模块
import threading
import time

'''
利用多线程完成 唱歌,跳舞功能
'''
def sing():
    # 获取当前线程
    current_thread = threading.current_thread()
    print("sing:",current_thread)
    for i in range(3):
        print("唱歌中")
        time.sleep(0.2)

def dance():
    # 获取当前线程
    current_thread = threading.current_thread()
    print("dance:", current_thread)
    for i in range(3):
        print("跳舞中")
        time.sleep(0.2)

# 2.创建子线程
if __name__ == '__main__':
    # 获取当前线程
    current_thread = threading.current_thread()
    print("main_thread:", current_thread)
    sing_thread = threading.Thread(target=sing, name="sing_thread")
    dance_thread = threading.Thread(target=dance, name="dance_thread")
    # 3.启动子线程执行对应的任务
    sing_thread.start()
    dance_thread.start()
    sing_thread.join()
    dance_thread.join()

线程执行带有参数的任务

线程执行带有参数的任务介绍

image-20240917153221309

args参数的使用

import threading
import time

def show_info(name,age):
    print("name: %s age: %d" % (name,age))


if __name__ == '__main__':
    # 创建子线程
    # 以元组方式传参,一一对应
    # sub_thread = threading.Thread(target=show_info,args=("高菲",20))
    sub_thread = threading.Thread(target=show_info,kwargs=({"name":"高菲","age":18}))
    # 启动线程
    sub_thread.start()

线程的注意点

image-20240917153921184

线程之间执行是无序的

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()
        
'具体哪个线程执行是由cpu调度的'

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

import threading
import time


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


if __name__ == '__main__':
    # 创建子线程
    # daemon = Ture 创建守护进程守护主线程
    sub_thread = threading.Thread(target=task,daemon=True)
    # 还有设置守护进程的方式
    # sub_thread.setDaemon(True)
    sub_thread.start()

    # 让主线程延时执行1S
    time.sleep(1)

    print("over")

把子线程设置成为守护线程即可

线程之间是可以共享全局变量的

import threading
import time

# 定义全局变量
g_list = []

# 添加任务
def add_data():
    for i in range(3):
        # 没循环一次把数据添加到全局变量
        g_list.append(i)
        print("add: ",i)
        time.sleep(0.2)
    # 代码执行到此,说明添加数据完成
    print("添加数据完成:",g_list)

# 读取数据
def read_data():
    print("read: ",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()
    read_thread.join()

# 结论:线程之间共享全局变量

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

image-20240917160608809

import threading


# 定义全局变量
g_num = 0

def task1():
    for i in range(1000000):
        # 没循环一次给全局变量加1
        global g_num # 表示要修改全局变量的内存地址
        g_num = g_num + 1 # g_num += 1

    # 代码执行到此
    print("task1:",g_num)

def task2():
    for i in range(1000000):
        # 没循环一次给全局变量加1
        global g_num # 表示要修改全局变量的内存地址
        g_num = g_num + 1 # g_num += 1

    # 代码执行到此
    print("task2:",g_num)

if __name__ == '__main__':
    # 创建2个子线程
    first_thread = threading.Thread(target=task1)
    second_thread = threading.Thread(target=task2)
    first_thread.start()
    # 线程等待,让第一个线程先执行,然后让第二个线程再执行
    first_thread.join() # 主线程等待第一个子线程执行完成以后代码再继续执行
    second_thread.start()

image-20240917161822033

互斥锁

image-20240917171429030

image-20240917171450952

互斥锁的使用

image-20240917171512767

image-20240917171555244

image-20240917171613396

使用互斥锁完成2个线程对同一个全局变量各加100万次的操作

import threading


# 定义全局变量
g_num = 0

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

def task1():
    # 上锁
    lock.acquire()
    for i in range(1000000):
        # 没循环一次给全局变量加1
        global g_num # 表示要修改全局变量的内存地址
        g_num = g_num + 1 # g_num += 1

    # 代码执行到此
    print("task1:",g_num)
    # 释放锁
    lock.release()

def task2():
    # 上锁
    lock.acquire()
    for i in range(1000000):
        # 没循环一次给全局变量加1
        global g_num # 表示要修改全局变量的内存地址
        g_num = g_num + 1 # g_num += 1

    # 代码执行到此
    print("task2:",g_num)
    # 释放锁
    lock.release()

if __name__ == '__main__':
    # 创建2个子线程
    first_thread = threading.Thread(target=task1)
    second_thread = threading.Thread(target=task2)
    first_thread.start()

    second_thread.start()
    # 互斥锁可以保证同一时刻只有一个线程去执行代码,能够保证全局变量的数据没有问题
    # 线程等待和互斥锁都是把多任务改成单任务去执行,保证了数据的准确性,但是执行性能会下降
   

小结

image-20240917174207751

死锁

死锁:一直等待对方是否锁的情景就是死锁

image-20240917174604017

# 死锁: 一直等待对方是否锁的情景就是死锁
import threading

# 创建互斥锁
lock = threading.Lock()
# 需求:多线程同时根据下标在列表中取值,要保证同一时刻只能有一个线程去取值
def get_value(index):
    # 上锁
    lock.acquire()
    my_list=[1,4,6]
    # 判断下标是否越界
    if index >= len(my_list):
        print("下标越界:",index)
        # 取值不成功也需要释放互斥锁,不影响后面的线程去取值
        lock.release()
        return
    # 根据下标取值
    value = my_list[index]
    print(value)
    #释放锁
    lock.release()

if __name__ == '__main__':
    # 创建大量线程,同时执行根据下标取值的任务
    for i in range(10):
        # 没循环一次,创建一个子线程
        sub_thread = threading.Thread(target=get_value, args=(i,))
        # 启动
        sub_thread.start()

进程和线程的对比

关系对比

image-20240917175841169

区别对比

image-20240917180032120

优缺点对比

image-20240917180846203

小结

image-20240917180925578

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不冤不乐

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

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

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

打赏作者

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

抵扣说明:

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

余额充值