python并发编程

目录

1、什么是多任务处理

2、简述多进程

3、创建一个进程

4、Process中常用的方法和属性

5、多个进程的运行问题

6、使用Process子类创建对象

7、进程池


1、什么是多任务处理

        一台计算机可以同时处理多个任务,叫做多任务处理。实现多任务有三种方式:多进程、多线程、协程。

        单核CPU在处理多个任务时,比如有QQ,微信,网易云音乐,QQ只占用CPU的0.01秒,然后QQ会切出去,微信再占用0.01秒,然后微信会被切出去,网易云音乐再占用0.01s,CPU频繁快速地切换,使人无法察觉,感觉这三个任务时同时进行的。所以当开很多任务时,电脑会变卡。           (ps:有几个CPU就叫做几核CPU)

        补充两个概念:                       

        ①高并发:

            任务管理器的进程可以理解为正在运行当中的程序。

            进程数远远大于CPU的核数,此时叫做高并发。比如图中有100多个进程,但电脑是双核CPU,此时就是高并发。

          ②高并行:CPU的核数多,同时运行的程序多。比如有50核,可以同时有50个程序运行

  

2、简述多进程

        多个任务同时在跑叫做多进程

        程序:一个指令的集合。比如我们写的python代码,每一行看做一个指令,这么多指令放在一起,没有执行,就是程序 

        进程:正在执行的程序

        一般的python代码都是从上往下执行(单进程),不是多进程

        程序开始运行时,首先创建一个主进程(父进程),在主进程下,可以创建新的进程(子进程),子进程不能创建新的子进程,子进程依赖于主进程,主进程结束,子进程也会结束。python提供了多进程包multiprocessing,借助这个包,可以完成从单进程到并发执行的转换

3、创建一个进程

首先应该调用包

import multiprocessing

但因为包里很多东西不会用到,所以一般采用

from multiprocessing import 想引用的玩意

 故,如下:

from multiprocessing import Process

        Process是进程类,通过它,可以创建一个进程对象,就可以让进程对象去执行某个函数,Process中的参数target应赋值函数名,告诉计算机当前创建的进程对象是运行哪个函数;参数args放置被执行函数的参数,args应赋值位置参数元组(给函数参数首先是一个元组,另外在位置上、数量上、类型上要一一对应),注意若元组只有一个元素,则需要在后面加逗号。

        包中的start()会执行相应的函数;因为主进程结束,子进程也会结束,所以用join()保证子进程的完整运行,防止主进程结束,而子进程未运行完。

        在win系统,python3情况下,还应添加如下,才能保证正常运行

if __name__ == "__main__":

所以代码如下:

from multiprocessing import Process

def run1():
    print('wulala')

def run2():
    print('hahaha')

def run3(name):
    print('My name is %s'%(name))

if __name__ == "__main__":
    p1 = Process(target=run1,)
    p1.start()#调用start,会执行对应的run1函数
    p1.join()#保证子进程的完成
    p2 = Process(target=run2)
    p2.start()
    p2.join()
    p3 = Process(target=run3,args=('Python',))
    p3.start()
    p3.join()

运行的结果如下:

画一个执行顺序:

 Process中还有一个属性是name,可有可无,是子进程的名称,如果不被设置,则系统自己设置

如,修改/添加以下代码:

    p1 = Process(name="进程1",target=run1,)
    print(p1.name)

 结果为:

至于     if __name__ == "__main__":                       

.py文件有两种作用,一种是直接作为程序来运行,二种可以导入到其他python程序当中。p1,p2,p3子进程是由主进程创建的,创建的时候,子进程会默认导入主进程的所有代码,就会变成递归创建(子进程创建子进程是不允许的),而且没有结束条件,会报错。__name__会表示当前模块的名字,如果在主进程里,则__name__就是__main__,子进程里的模块名不是__main__,则不被执行。

4、Process中常用的方法和属性

---p.start()                启动进程,调用该子进程中的p.run()

---p.run()                  进程启动时运行的方法,正式它去调用target指定的函数。

---p.terminate()        强制终止进程p,不会进行任何清理操作。(仍占用内存)

---p.is_alive()           如果p仍然运行,返回True,用来判断进程是否仍运行

---p.join([timeout])   主进程等待p终止,timeout是可选的超时时间。阻塞住主进程,在等待子进程结束,然后再往下执行

---name                   当前进程实例的别名,默认为Process-N,N是从1开始递增的整数

---pid                       当前进程实例的PID值。系统用来区分不同的进程

5、多个进程的运行问题

from multiprocessing import Process

num = 1

def run1():
    global num
    num += 5
    print("子进程1中num=%d"%(num))

def run2():
    global num
    num += 10
    print("子进程2中num=%d"%(num))

if __name__ == "__main__":
    print("父进程启动")
    p1 = Process(target=run1)
    p2 = Process(target=run2)
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print(num)

运行结果为:

多进程之间,调用的时候是无序的。如果一定想p1执行完在执行p2,则在p1.start()后直接加join()

p1.start()之后并不是马上执行p1,而是等待系统调度,等待时,主进程会继续往下走,若又一个子进程等待调度,这两个子进程谁先开始不确定。因为此时代码较少,所以大部分情况是p1先执行。

至于结果是11,6,1,原因是多个进程之间的数据默认不共享,创建子进程时,导入父进程所有代码,num都是1,所以6,11,主进程依然是1。

6、使用Process子类创建对象

import multiprocessing
import time

class MyProcess(multiprocessing.Process):
    def run(self):#方法重写
        n = 5
        while n > 0:
            print(n)
            time.sleep(1)
            n -= 1

if __name__ == "__main__":
    p = MyProcess()#不用指明target
    p.start()
    p.join()

因为是run()函数使得子进程运行,所以重新写run()函数。因为将代码写入了run,已经指明了run 函数要执行的内容,所以不用写target。

7、进程池

Process、子类、进程池是创建子进程的三种方法,子进程数量多时候,适用进程池。

from multiprocessing import Pool
import time

def work(num):
    print(num)
    time.sleep(1)

if __name__ == "__main__":
    po = Pool(2)#两个进程同时运行
    for i in range(20):#有20个进程被进程池创建,同时最多只能两个运行
        po.apply_async(work,(i,))#是一个元组
    po.close()#关闭进程池,表示不再接受新请求,原进程不会删除
    po.join()#一定要在close后面

po = Pool(10)表示同时有10个进程池创建出来的进程在运行,如果不赋值,则默认为电脑CPU数

po.apply_async(work,(i,))如果执行的函数有参数,则逗号分隔,添加元组

进程池创建的函数,不用再调用start,会自己排队去执行

multiprocessing.Pool常用的函数

  apply_async:使用非阻塞方式调用函数                   

  apply:使用阻塞方式调用函数,同一时间只允许一个子进程运行   

  close:关闭Pool,使其不再接受新的任务   

  terminate:不管任务是否完成,立即终止       

  join:用法和之前相同

进程池创建的函数,不用再调用start,会自己排队去执行

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值