python 进程 线程 总结

进程

多任务

操作系统上运行多个任务同时运行

并行:

任务的数量小于cpu的核心数量【理想型】

并发:

任务的数量大于cpu的核心数量。【现实】

单核cpu运行

操作系统处理方式:每个任务轮流交替执行,由于不同的任务之间的切换速度很快,导致肉眼无法识别

进程

一个任务即一个进程【Process】。
exp:打开一个浏览器。打开一个word启动一个word进程。但是,一个进程可以干很多事情(读、写等等),在一个进程的内部,要同时干很多件事情,就需要同时执行多个子任务,将一个进程中的多个子任务被称为线程【Thread】

进程的特点

a、独立性:不同的进程之间是相互独立的,在操作系统中互不影响,相互之间资源不共享
b、动态性:进程一旦被启动之后,在操作系统中并不是静止不动的,一直处于动态状态
c、并发性:在操作系统交替执行
多任务的实现方式:

多任务的实现方式

a、多进程模式:启动多个进程,每个进程虽然只有一个线程,通过改线程执行一个任务
b、多线程模式:启动一个进程,改改进程启动多个线程,每个线程执行一个任务
c、协程模式:yield【函数生成器】
d、多进程模式+多线程模式:启动多个进程,每个进程启动多个线程,执行的任务的数量会更多

multiprocessing模块(多进程模块)

Process

通过Process创建对象实现多进程

import time
import os
from multiprocessing import Process


def son():
    # os,getpid 显示当前进程  系统编号
    print("子进程开始了:%s\n" % os.getpid())
    time.sleep(1)
    print("子进程干活了\n")
    time.sleep(1)
    print("子进程结束了:%s\n" % os.getpid())
    time.sleep(2)


if __name__ == '__main__':
    print("主进程开始:%s\n" % os.getpid())

    # 创建子进程对象
    p = Process(target=son)
    # 启动子进程
    p.start()
    # p.join() # 可以修改进程执行顺序

    print("主进程结束了")

"""
主进程开始:7504

主进程结束了
子进程开始了:9692

子进程干活了

子进程结束了:9692

"""

"""
从结果上看出,执行的main和son()的是两个进程,
如果没有创建进程,直接执行son则线程号是一样的,
从上而下依次执行

p.join():修改执行顺序,父进程等待子进程结束后在继续执行

如果son有参数,则创建的时候也是需要以元组的形式传入。可以参考下面的进程池。


"""

Pool

pool:进程池 如果我们需要开启多个进程,可以通过Process一个个的创建,只是这样有点麻烦,Python内置了Pool这个类(object),用来创建多个进程,简化代码。
!注意:使用pool的时候先查看自己电脑是几核的(cpu内核数量,不懂自行百度),可以开启几个进程。

import time
import os
from multiprocessing import Pool


def run(mag):
    # for _ in range(5):
    print("Nice day~~~进程号:%s" % os.getpid())
    time.sleep(1)


if __name__ == '__main__':
    print("主进程开始~~~%s" % os.getpid())
    time.sleep(1)
    p = Pool(4)        # 开启 4 个进程
    for i in range(8): # 任务执行8次,
        p.apply_async(run, args=(i,)) # 执行任务

    p.close()
    p.join()

    print("主进程结束~~~%s" % os.getpid())

"""
p = pool(4) 这里表示我们允许(需要执行的函数)最多占用4个进程
p.apply_async(run,args=(i,))  调用Pool的方法,参数为一个函数,即run
args=(i,)是run函数需要的参数,必须以元组的形式传入。
p.close()表示待循环完成以后关闭进程池
p.join() 表示子进程要执行,类似于Process的start(),开启进程池并且改变顺序。
等待子进程全部执行完毕,主进程继续。
"""

"""
主进程开始~~~10404
Nice day~~~进程号:9180
Nice day~~~进程号:11740
Nice day~~~进程号:7112
Nice day~~~进程号:4508
Nice day~~~进程号:11740
Nice day~~~进程号:9180
Nice day~~~进程号:7112
Nice day~~~进程号:4508
主进程结束~~~10404
"""

封装进程对象

import time
import os
from multiprocessing import Pool, Process


class Defined(Process):
    def run(self) -> None:   # "->" 在python中叫“注解”,自行百度
        print("子进程开始:%s" % os.getpid())
        time.sleep(2)
        print("子进程结束:%s" % os.getpid())


if __name__ == '__main__':
     print("主进程启动:%s" % os.getpid())
     p = Defined()
     p.start()
     p.join()

     print("结束%s" % os.getpid())
"""
主进程启动:10780
子进程开始:11096
子进程结束:11096
结束10780
"""

进程间的通信

Process之间是需要相互通信的,操作系统提供了很多的办法实现进程间的通信,Python中的multiprocessing包装了底层的机制,提供了Queue【队列】,Pipes等多种方式实现交换数据

#需求:在主进程中创建两个子进程,一个进程用来向队列中写数据,另外一个进程用来从队列中读数据

from  multiprocessing import  Process,Queue
import  os,time,random

#1.写数据的任务
def write(queue):
    print("进程%s开始" % (os.getpid()))
    print("开始写入")

    for value in ["A","B","C"]:
        print("add %s to queue" % (value))

        #向队列中添加数据
        queue.put(value)

        #稍休息片刻,接着加
        time.sleep(random.random())

    print("进程%s结束" % (os.getpid()))

#2.读数据的任务
def read(queue):
    print("进程%s开始" % (os.getpid()))
    print("开始读取")

    n = 0
    while n < 3:
        #从队列中获取数据
        value = queue.get(True)
        print("get %s from queue" % (value))
        n += 1

    print("进程%s结束" % (os.getpid()))

#3.在主进程中分别创建子进程,执行相应的任务
if __name__ == "__main__":
    print("父进程启动")

    #3.1创建队列对象,并传参给每个进程
    q = Queue()

    #3.2创建进程对象,
    pr = Process(target=read,args=(q,))
    pw = Process(target=write,args=(q,))

    #3.3启动子进程,让进行数据的读写
    pw.start()
    pr.start()

    #3.4设置,让所有子进程结束之后,才结束父进程
    pr.join()
    pw.join()

    print("父进程结束")

线程

of:是进程的组成部分,一个进程可以有多个线程,每个线程去处理一个特定的子任务
注意:一个进程至少有一个子任务,否则改进程没有意义

线程的执行是抢占式的,多个线程在同一个进程中可以并发执行,其实就是在cpu之间进行的快速的切换【每个线程都有争抢时间片的机会,谁抢到时间片,则执行对应的线程,剩下的线程都会被挂起】
比如: 打开网易云----》 启动了一个进程
   播放歌曲和刷新歌词-----启动了两个线程
  
【面试题】进程和线程的关系
a、一个程序启动以后至少有一个进程
b、一个进程可以包含多个线程,但是至少需要一个线程,否则改进程没有意义
c、一个进程可以包含多个线程,线程之间是可以资源共享的
d、系统创建进程需要为该进程重新分配资源,而创建线程容易的多,因此使用多线程实现多任务比多进程实现效率更高。

创建线程

_thread模块:提供了低级别的,原始的线程【功能有限,底层采用的是c语言】
threading模块:高级模块,对_thread进行了封装,有_thread没有的功能

创建多线程

import time
import threading

def run():
    # for _ in range(5):
        print("Nice_day")
        time.sleep(1)


if __name__ == '__main__':
    se = threading.BoundedSemaphore(5)  # 有没有都一样,限制最大线程数为5
    t_list = []
    for i in range(5):
        t = threading.Thread(target=run)
        t_list.append(t)               # 将创建的线程保存在t_list中

    for j in t_list:
        j.start()              
    # run()
    print("主线程结束")
"""
代码解释:
单线程执行是一个执行完再接着执行下一个,如run()注释掉的for语句,一次接着一次打印。
如果我们同时创建多个线程,则一次性执行5次run()。
"""

线程锁

原因

线程的执行是抢占式的,多个线程在同一个进程中可以并发执行,其实就是在cpu之间进行的快速的切换【每个线程都有争抢时间片的机会,谁抢到时间片,则执行对应的线程,剩下的线程都会被挂起】

from  threading import  Thread,Lock

balance = 0

#创建一个锁对象
lock = Lock()

def change(n):
    global balance
    balance = balance + n
    balance = balance - n

#子线程的任务
def run(n):
    for _ in range(1000000):
        #获取锁
        lock.acquire()

        try:
            # 临界资源
            change(n)
        finally:
            #释放锁
            lock.release()


if __name__ == "__main__":

    t1 = Thread(target=run,args=(5,))
    t2 = Thread(target=run,args=(8,))

    t1.start()
    t2.start()

    t1.join()
    t2.join()

    #理论上,balance最终的结果为0
    print(balance)
from  threading import  Thread,Lock

balance = 0

#创建一个锁对象
lock = Lock()

def change(n):
    global balance
    balance = balance + n
    balance = balance - n

#子线程的任务
def run(n):
    for _ in range(1000000):
        #同样表示获取锁,但是,不用手动释放,当临界资源的代码执行完毕之后,锁会被自动释放
        with lock:
            # 临界资源
            change(n)

if __name__ == "__main__":

    t1 = Thread(target=run,args=(5,))
    t2 = Thread(target=run,args=(8,))

    t1.start()
    t2.start()

    t1.join()
    t2.join()

    #理论上,balance最终的结果为0
    print(balance)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值