Python之进程,线程,协程

目录

进程:

关于进程的理解:

进程的创建(进程间全局变量)

自定义进程:

进程池(阻塞式和非阻塞式-多进程):

队列:Queue

进程通信(queue):

线程:

对线程的简单理解:

线程优点:

关于线程的使用方法:

如何创建并使用线程?

线程(共享全局变量):

线程(锁问题):

多线程同步问题:

线程(死锁问题):

生产者和消费者问题(两线程间的通信):

协程:

协程的优点:

协程的实现(yield):

协程的实现(greenlet):

协程的实现(gevent+猴子补丁):

协程模拟下载多个任务:


进程:

关于进程的理解:

多任务概念:

        生活中,你可能一边听歌,一边写作业;一边上网,一边吃饭... 这些都是生活中的多任务场景。 电脑也可以执行多任务,比如你可以同时打开浏览器上网,听音乐,打开pycharm编写代码......

简单的说**多任务就是同一时间运行多个程序**

单核和多核:

--单核CPU实现多任务原理:操作系统轮流让各个任务交替进行,QQ执行2us,切换到微信,再执行2us,再切换到陌陌,执行2us...。 表面是看,每个任务反复执行下去,但是CPU调度执行速度太快了,导致我们感觉所以任务都在同时执行一样。

--多核CPU实现多任务原理:真正的并行执行多任务只能在多核CPU上实现,但是由于任务数量远远多于CPU的核心数量, 所以,操作系统也会自动把很多任务调度到每个核心上执行。

实现多任务的方式:

--多进程模式

--多线程模式

--协程

进程 > 线程 > 协程

大致上来说,进程是线程的容器,是程序的实体;进程是分配空间,线程是分配时间。

进程优点:

稳定性高,一个进程崩溃了不会影响其他进程

进程缺点:

1.创建进程开销巨大

2.操作系统能同时运行的进程数目有限

        在linux下可以使用fork()函数创建进程;在windows系统上可以引用multiprocessing模块,创建进程,我们可以使用multiprocessing模块中Process类创建新的进程。python里的fork()在os模块里。

进程的创建(进程间全局变量)

from multiprocessing import Process      #导入模块

process = Process(target = 函数,name = 进程名,args = (给函数传递的参数))

process.start()        #启动进程并执行任务

process.run()          #只是执行了任务没有启动进程

process.join()         #起到堵塞的作用,完成上面的任务后才可以往下进行

terminate()              #终止

os模块里的getpid()可以获取当前进程号。

###举例说明

from multiprocessing import Process  # 使用该模块创建进程
from time import sleep
import os

m = 1  # 不可变类型


def task1(s, name):
    global m
    while True:
        m += 1
        sleep(s)
        print('这是任务1...', os.getpid(), m)  # 两进程并不共享全局变量m,相当于每个进程单独拥有一份m


def task2(s, name):
    global m
    while True:
        m += 1
        sleep(s)
        print('这是任务2...', os.getpid(), m)


if __name__ == '__main__':
    number = 0

    print(os.getpid())
    # 子进程
    p = Process(target=task1, name='任务1', args=(1, 'aa'))
    p.start()
    print(p.name)  # 打印动作在主进程里
    p1 = Process(target=task2, name='任务2', args=(2, 'bb'))
    p1.start()
    print(p1.name)  # 打印动作在主进程里

输出:

7368
任务1
任务2
这是任务1... 15988 2
这是任务2... 1692 2
这是任务1... 15988 3
这是任务1... 15988 4
这是任务2... 1692 3
这是任务1... 15988 5
这是任务1... 15988 6
这是任务2... 1692 4
这是任务1... 15988 7
这是任务1... 15988 8
这是任务2... 1692 5
这是任务1... 15988 9
这是任务1... 15988 10
...
...
...

1.该输出条件没有限制,将一直输出。

2.主进程里声明了整型变量m,在子进程里的m仅共享初始化的m,值为1;而后对m的操作并不共享,说明在进程里的变量具有独立性。

自定义进程:

当父类进程里的内容不满足需求时,我们可以自定义进程。

1.继承主进程(super的使用)

2.重写run()方法

3.创建进程对象并启动(start)进程,start的调用会默认执行自定义进程里的run()函数

###举例说明

from multiprocessing import Process
from time import sleep
class MyProcess(Process):  # 继承了主进程
    def __init__(self,name):
        super(MyProcess,self).__init__()
        self.name = name

    # 重写run方法
    def run(self):
        n = 1
        while True:
            sleep(1)
            print('{}____自定义进程:{}'.format(n,self.name))
            n += 1

if __name__ == '__main__':
    p = MyProcess('小明')
    p.start()
    p1 = MyProcess('小红')
    p1.start()

输出:

1____自定义进程:小明
1____自定义进程:小红
2____自定义进程:小明
2____自定义进程:小红
3____自定义进程:小明
3____自定义进程:小红
4____自定义进程:小明
4____自定义进程:小红
...
...
...

        以上代码仅简单作一个自定义进程,不做过多解释;其中输出没有设置限制条件,故会一直执行;要注意定义进程类时继承主进程,看情况写初始化函数 __init__(),需重写run()方法。

进程池(阻塞式和非阻塞式-多进程):

        当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态生成多个进程, 但是如果是成百甚至上千个目标时,手动的去创建进程的工作量巨大,此时就可以用到multiprocessing模块里的(进程池)Pool方法。

        初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满 那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待, 直到池中有进程结束,才会创建新的进程来执行。

1.进程池中的进程在创建的时候就已经分配好了进程号,任务的迭代不影响进程号的改变。

2.进程池的存活依赖于主进程,同生共死。

非阻塞式:全部添加到队列中,立刻返回,并不等待其他进程执行完毕才结束, 但是回调函数是等待任务完成之后才会调用执行。

pool = Pool(5)    #创建了可容纳5个进程的进程池

pool.apply_async(func=函数名,args=(参数,),callback=回调函数名)    #当函数参数只有一个,在参数后面加逗号

非阻塞式标志是apply_asynccallback回调函数的意思,等待任务完成后执行。

###举例说明

import os
import time
import random
from multiprocessing import Pool
#非阻塞式进程

def task(task_name):

    print('开始做任务啦!',task_name)
    start = time.time() #算出当前时间
    #使用sleep()
    time.sleep(random.random()*2)
    end = time.time()
    return '完成任务{}  用时:{},进程ID:{}'.format(task_name,end-start,os.getpid())

container = []
def callback_func(n):
    container.append(n)

if __name__ == '__main__':
    #创建了容量为5的进程池,在创建的时候就已经分配好了进程号,任务的迭代不影响进程号的改变
    pool = Pool(5)   #进程池的存活依赖于主进程,同生共死
    tasks = ['LOL','洗衣服','吃饭','遛狗','听歌','睡觉']

    for i in tasks:
        #进程池里可装5个进程,但是有6个任务所以最后一个任务需要等其他进程结束才能开始,需要排队
        pool.apply_async(func=task,args=(i,),callback=callback_func)  #使用非阻塞模式(非同步);callback为回调,当func执行完毕后,return的东西会给到回调函数callback


    pool.close()  #添加任务结束;关闭进程池,不再接收新的请求。
    pool.join()   #堵住主进程,必须在完成任务后才能往下进行

    for i in container:
        print(i)
    print('OVER!')

输出:

开始做任务啦! LOL
开始做任务啦! 洗衣服
开始做任务啦! 吃饭
开始做任务啦! 遛狗
开始做任务啦!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

谁动了我的马卡龙

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

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

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

打赏作者

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

抵扣说明:

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

余额充值