python-进程、线程

一、进程

进程:资源分配的最小单位(QQ、微信)

多进程:程序自动创建主进程;自己创建子进程

1. 导入进程模块

import multiprocessing

 2. 创建进程对象

coding_process = multiprocessing.Process(target=coding)  # target=函数名
music_process = multiprocessing.Process(target=music)

3. 启动进程 

coding_process.start()
music_process.start()

对比:

1. 主进程

import time
def coding():
    for i in range(3):
        print("coding...")
        time.sleep(0.2)


def music():
    for i in range(3):
        print("music...")
        time.sleep(0.2)

coding()

musci()

结果:  

2. 多进程

扫描二维码关注公众号,回复: 14639315 查看本文章
# 代码只能作为脚本直接执行,import 到其他脚本中是不会被执行的
if __name__ == '__main__':
    # 创建进程对象
    coding_process = multiprocessing.Process(target=coding)  # target=函数名
    music_process = multiprocessing.Process(target=music)
    
# 启动进程
    coding_process.start()
    music_process.start()

结果: 

 二、进程(参数)

args:以元组方式给执行任务传参                           args=(a, b)           #a、b为两个参数

kwargs: 以字典方式给执行任务传参                       kwargs={}              #

 三、进程编号

os.getppid() 获取当前进程编号

os.getppid() 获取当前父进程编号

import os

def work():  # 定义函数

   print("work进程编号:", os.getpid())

   print("work父进程编号:", os.getppid())

def coding():
    print("coding %d" % os.getpid())
    print("coding %d" % os.getppid())  # 父进程
    for i in range(3):
        print("coding...")
        time.sleep(0.2)

def music():
    print("music %d" % os.getpid())
    print("music %d" % os.getppid()  # 父进程
    for i in range(3):
        print("music...")
        time.sleep(0.2)
if __name__ == '__main__':  #主进程
    print("music %d" % os.getpid())
    print("music %d" % os.getppid())
    # 创建进程对象
    coding_process = multiprocessing.Process(target=coding)  # target=函数名
    music_process = multiprocessing.Process(target=music) #2为实参
    # 启动进程
    coding_process.start()
    music_process.start()

 说明:coding和music是由主进程创建的子进程

四、 进程间不共享全局变量

五、主进程、子进程结束顺序

主进程会等所有子进程执行完毕后再执行

守护子进程:主进程执行完毕后不管子进程是否执行完毕都会销毁。

work_process.daemon = True

手动销毁子进程: work_process.terminate()

if __name__ == "__main__":
    # 创建子进程
    work_process = multiprocessing.Process(target=work)
1) # 设置守护主进程
    work_process.daemon = True
    # 启动子进程
    work_process.start()
    # 延时1秒
    time.sleep(1)
2)  # 手动结束
    work_process.terminate()
    
    print("主进程执行完毕")

六、线程

多线程是实现多任务的一种方式

程序执行的最小单位,一个进程里最少有一个进程来执行

同一进程的多个线程共享所拥有的的全部资源

线程创建步骤:

1. 导入线程模块

import threading

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

线程对象=threading.Thread(target=任务名) # 函数名

3. 启动线程执行任务

线程对象.start()

1. 线程(同进程)

dance = threading.Thread(target=sing, kwargs={"count": 5})

sing = threading.Thread(target=sing, arg(5, ))

线程执行无序

import threading
import time

def task():
    time.sleep(1)
    # threading.current_thread() 当前线程 .name 获取线程名字
    print(f"当前的线程是 {threading.current_thread().name}")

if __name__ == '__main__':

    print(f"当前的主线程: {threading.current_thread().name}")
    # 多个线程
    for _ in range(5):  # _也是变量
        # 创建子线程
        sub_thread = threading.Thread(target=task)
        sub_thread.start()

 结果

2. 主线程会等待所有子线程运行结束后再执行

3. 线程之前共享全局变量

1)资源竞争:当多个线程调用一个全局变量资源时,全局变量资源不断的被修改,调用过程存在资源竞争

解决方法: 采用全局变量锁每次线程调用后,将该资源上锁,不允许再被调用,只有调用结束后打开锁,保证全局变量资源的安全。

# 创建锁

mutex = threading.Lock()

# 锁定

mutex.acquire()

# 释放

mutex.release()

注意:如果aquire调用前已有互斥锁,则会堵塞,等待解锁后,才会调用

import threading

# 定义全局变量

g_num = 0

# 创建互斥锁

lock = threading.Lock()

# 循环一次给全局变量加1

def sum_1():

# 上锁

     lock.aquire()

     for i in range(1000000):

           global g_num  # 全局变量

           g_num += 1

    print("sum1:", g_num)

#释放锁

      lock.release()

# 循环一次给全局变量加1

def sum_2():

# 上锁

     lock.aquire()

     for i in range(1000000):

           global g_num  # 全局变量

           g_num += 1

    print("sum2:", g_num)

#释放锁

      lock.release()


if __name__ == '__main__':
    # 创建两个线程
    first_thread = threading.Thread(target=sum_num1)
    second_thread = threading.Thread(target=sum_num2)  
    # 启动线程
    first_thread.start()
    second_thread.start()

总结:

1. 互斥锁可以保证多个线程访问同一个全局变量时不会出现错误

2. 加上互斥锁后,多任务变为单任务,性能下降

3. 互斥锁没使用好容易出现死锁情况

2)死锁

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

死锁会造成程序无法响应

例如:线程A的互斥锁未释放就让线程B的互斥锁上锁,等待资源释放

解决方案:

  • 程序设计时要尽量避免(银行家算法)
  • 添加超时时间等

银行家算法的核心思想:每次资源分配前判断此次分配是否会导致系统进入不安全状态,以此来决定是否答应资源分配请求

总结

关系:

1. 线程是依附在线程里的,没有进程就没有线程

2. 一个进程默认提供一条线程,进程可以创建多个线程

对比:

1. 进程之间不共享全局变量

2. 线程之间共享全局变量,但注意资源竞争问题,解决方法是互斥锁,但要注意死锁

3. 创建线程资源开销比创建线程资源开销大

4. 进程是操作系统分配资源的基本单位,线程是CPU调度的基本单位

优缺:

进程:多核,资源开销大

线程:不能多核,资源开销小

七、进程之间的通信

每个进程的用户地址空间都是独立的,一般而言是不能互相访问的,但内核空间是每个进程都共享的, 所以进程之间要通信必须通过内核。

每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信机制。

主要的过程如下图所示(来自知乎):

信号量:信号量的工作机制(因为真的很简单),可以直接理解成计数器(当然其实加锁的时候肯定不能这么简单,不只只是信号量了),信号量会有初值(>0),每当有进程申请使用信号量,通过一个P操作来对信号量进行-1操作,当计数器减到0的时候就说明没有资源了,其他进程要想访问就必须等待(具体怎么等还有说法,比如忙等待或者睡眠),当该进程执行完这段工作(我们称之为临界区)之后,就会执行V操作来对信号量进行+1操作

猜你喜欢

转载自blog.csdn.net/Kiraxqc/article/details/125915810
今日推荐