Python中的进程和线程

Python中的进程和线程

一.进程的概念
一个正在运行的程序或者软件就是一个进程,它是操作系统进行资源分配的基本单位,也就是说每启动一个进程,操作系统都会给其分配一定的运行资源(内存资源)保证进程的运行。
注意:
一个程序运行后至少有一个进程,一个进程默认有一个线程,进程里面可以创建多个线程,线程是依附在进程里面的,没有进程就没有线程.
并行:多个cpu同时处理多个程序
并发:一个cpu在一个很小的时间段之内在多个程序之间来回切换执行,切换的时间由操作系统决定,但时间比较小
进程的作用:

单进程效果图:
在这里插入图片描述
多进程效果图:
在这里插入图片描述
进程是操作系统分配资源的基本单位
进程是python实现多重任务的一种方式

二.多进程的使用
1.Process功能

用于直接定义进程

from multiprocessing import Process
p = Process(target=function,args,kwargs)

Process的参数组成:
Process([group [, target [, name [, args [, kwargs]]]]])

group:指定进程组,目前只能使用None
target:执行的目标任务名,后面一般接的是进程的函数名
name:进程函数名字
args:以元组方式给执行任务传参
kwargs: 以字典方式给执行任务传参

# 1.导入多进程的包
import os
import time
from multiprocessing import Process

# 2.定义两个函数,用多进程的方式进行处理

def run1(name):
    # 获取子进程的进程编号
    print('run1--',name)

def run2(name,age=0):
    print('run2--',name,age)

if __name__ == '__main__':
    # 1.导包
    import time
    from multiprocessing import Process

    # 3.通过process创建,target后面接的是函数名称
    # 给进程传递非关键字参数,需要定义args
    # 给进程传递关键字参数,需要定义kwargs
    p1 = Process(target=run1,args=('a',))  # args可以是元组或者列表,但一定是这两个形式之一
    p2 = Process(target=run2,args=('b',),kwargs={'age':20})  # 给进程的关键字传参,kwargs,类型是字典型


    # 4.执行进程
    p1.start()
    p2.start()

    print('main')
    # join阻塞进程:上面进程执行完之后才会执行下面进程
    p1.join()
    p2.join()
    print('main')
2.获取进程编号

目的:验证主进程和子进程的关系,可以得知子进程是由那个主进程创建出来的。

方法:

import os
# 1.获取子进程的编号
pid = os.getpid()

# 2.获取父进程的编号
ppid = os.getppid()
3.守护进程

目的:验证主进程和子进程的关系,可以得知子进程是由那个主进程创建出来的。(主进程结束,子进程就结束了)

设置方式:子进程对象.daemon = True

p.daemon = True
4.进程之间的数据共享

原则上,各进程之间的数据是不共享的,但可以创建参数共享池来进行个进程之间的共享.

三种方式:manager,queue(队列),pipe(管道)
manager:

from multiprocessing import Process, Manager,Queue,Pipe

# 1. manager方式
    with Manager() as manager:
        lis = manager.list()  # 使用Manger来对参数进行管理
        p1 = Process(target=run1, args=(lis,))
        p2 = Process(target=run2, args=(lis,))
# 2.queue方式
def run1(q):
    q.put('run1') # 调用queue的方法输出数据
    time.sleep(1)
    print(q.get()) # 获取数据

def run2(q):
    print(q.get()) # 获取数据
    q.put('run2')  # 出数据

if __name__ == '__main__':

    # 实例化队列,队列的存储方式是一头进一头出,先进先出,后进后出
    q = Queue()

    # 3,通过Process创建进程
    p1 = Process(target=run1, args=(q,))
    p2 = Process(target=run2, args=(q,))
    # 4,执行进程
    p1.start()
    p2.start()
    p1.join()
    p2.join()
# 3.pipe方式
def run1(pi1):
    pi1.send('run1')
    time.sleep(1)
    print(pi1.recv())

def run2(pi2):
    print(pi2.recv())  # 获取数据,如果此时管道中没有数据,那么pi2会一直等待,会使得管道堵塞,相当于一直占用管道
    pi2.send('run2')   # 发送数据

if __name__ == '__main__':

    # 实例化管道
    pi1, pi2 = Pipe()

    # 3,通过Process创建进程
    p1 = Process(target=run1, args=(pi1,))
    p2 = Process(target=run2, args=(pi2,))
    # 4,执行进程
    p1.start()
    p2.start()
    p1.join()
    p2.join()
5.进程池

目的:当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态成生多个进程,但如果是上百甚至上千个目标,手动的去创建进程的工作量巨大,而且频繁的创建和销毁进程,会消耗非常大的资源。所以就可以用到multiprocessing模块提供的Pool方法

    # 创建进程池
    # 设置进程池内的进程个数
    pool = Pool(3)

    for i in range(10):
        pool.apply_async(run1)  # 异步将进程传入进程池中

    # 必须关闭进程池
    pool.close()

    # 4.必须等待所有的请求全部执行完成时候,再结束主进程
    pool.join()
三.线程
1.概念

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

由于python解释器中存在GIL锁,所以python中的多线程是伪多线程,也就是cpu不能并行执行,只能并发执行

2.线程的使用

使用Threading模块,其用法跟Process是一样的

if __name__ == '__main__':
    # 扩展: 获取当前线程
    # print("当前执行的线程为:", threading.current_thread())
    # target: 线程执行的函数名
    sing_thread = threading.Thread(target=sing,args,kwargs)

    # 创建跳舞的线程
    dance_thread = threading.Thread(target=dance)
    
    t1.setDaemon(True)  # 守护进程
    t2.setDaemon(True)
    # 开启线程
    sing_thread.start()
    dance_thread.start()
3.线程间的数据是共享的

准确地说,线程间大的全局变量是共享的,而且多个线程之间是并发关系,即一个CPU在多个线程之中会来回切换运行,因此也可能带来数据的错乱,这时候需要加上锁来防止数据错乱:

from threading import  Thread,Lock
import time
lock = Lock()
a = 0
def run1():
    global a

    for i in range(1000000):
        # 加锁
        lock.acquire()
        a += 1
        # 释放锁
        lock.release()

def run2():
    global a
    for i in range(1000000):
        lock.acquire()
        a += 1
        lock.release()

if __name__ == '__main__':
    t1 = Thread(target=run1)
    t2 = Thread(target=run2)

    t1.start()
    t2.start()

    t1.join()
    t2.join()
    print(a)
# 如果没有加锁,则a的值应该小于2000000
总结
  1. 进程和线程的对比的三个方向
    关系对比
    区别对比
    优缺点对比

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

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

  4. 优缺点对比
    进程优缺点:
    优点:可以用多核
    缺点:资源开销大 # 每个进程都需要不同的内存
    线程优缺点:
    优点:资源开销小 # 线程可以共享内存,共享数据
    缺点:不能使用多核

  5. 使用场景
    计算密集型:CPU 长时间满负荷运行, 如图像处理、大数据运算、科学运算等。使用多进程
    I/O 密集型:网络 IO, 文件 IO, 设备 IO 等。使用多线程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值