Day06 python进程与线程

Day06 python的进程

一脸懵的一天

  • 什么是进程进程(Process) 是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。。

  • 狭义定义:进程是程序的一次执行,每个进程都有自己的地址空间、内存、数据栈及其他记录其运行轨迹的辅助数据。

  • 广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
    操作系统管理在其上运行的所有进程,并为这些进程公平的分配时间,进程也可以通过fork和spawn操作来完成其他的任务。
    不过各个进程都有自己的内存空间、数据栈等,所以只能使用进程间通讯,而不能直接共享信息。
    Python中的多进程
    Process类的构造方法:
    help(multiprocessing.Process)

init(self, group=None, target=None, name=None, args=(), kwargs={})

参数说明:

group:进程所属组,基本不用。

target:表示调用对象,一般为函数。

args:表示调用对象的位置参数元组。

name:进程别名。

kwargs:表示调用对象的字典。

创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,并用其start()方法启动。
先来比较一下使用多进程和不使用多进程有什么差别,我们来做一个小例子

from random import randint
from time import time, sleep


def download_task(filename):
    print('开始下载%s...' % filename)
    time_to_download = randint(5, 10)
    sleep(time_to_download)
    print('%s下载完成! 耗费了%d秒' % (filename, time_to_download))


def main():
    start = time()
    download_task('Python从入门到住院.pdf')
    download_task('Peking Hot.avi')
    end = time()
    print('总共耗费了%.2f秒.' % (end - start))


if __name__ == '__main__':
    main()

结果是:
在这里插入图片描述
接下来我们使用多进程的方式将两个下载任务放到不同的进程中,代码如下所示。

from multiprocessing import Process
from os import getpid
from random import randint
from time import time, sleep


def download_task(filename):
    print('启动下载进程,进程号[%d].' % getpid())
    print('开始下载%s...' % filename)
    time_to_download = randint(5, 10)
    sleep(time_to_download)
    print('%s下载完成! 耗费了%d秒' % (filename, time_to_download))

    
def main():
    start = time()
    p1 = Process(target=download_task, args=('Python从入门到住院.pdf', ))
    p1.start()
    p2 = Process(target=download_task, args=('Peking Hot.avi', ))
    p2.start()
    p1.join()
    p2.join()
    end = time()
    print('总共耗费了%.2f秒.' % (end - start))


if __name__ == '__main__':
    main()

结果输出是:
在这里插入图片描述
对比之下多线程要比单线程速度更快。
在Python程序中的进程操作 :在Python中操作、管理进程需要借助multiprocessing包。该包主要包括了四部分:创建进程部分,进程同步部分,进程池部分,进程之间数据共享。
【1】multiprocessing.process模块

1)process模块是一个创建进程的模块,由于Python是跨平台的,自然也应该提供一个跨平台的多进程支持。multiprocessing模块就是跨平台版本的多进程模块。借助这个模块,就可以完成进程的创建。

from multipprocessing import Process
import time

def funt(t):
	time.sleep(t)
	print('这里是子进程')
#在创建子进程的时候会自动import启动它这个文件,而在import的时候又执行了整个文件。
#因此如果将process()直接写在文件中就会无限递归创建子进程报错。
#所以必须把创建子进程的部分使用if __name__=='__main__':判断保护起来,import的时候,
#就不会递归运行了,
#创建子进程,target要执行的任务;args传递参数,元祖形式,必须有逗号;
 # group 未使用,值始终为None;kwargs表示调用对象的字典;name为子进程的名称
    p = Process(target = func,args=(1,),name="子进程")
    # 启动进程,并调用该子进程中的p.run()
    p.start()
    print("这里是父进程!")

结果是:
在这里插入图片描述
进程属性:

#进程属性
from multiprocessing import Process
import time
 
def func(t):
    print("子进程开始!")
    time.sleep(t)
    print("子进程结束!")
 
if __name__ == '__main__':
    p = Process(target = func,args=(1,),name="子进程")
    p.start()
    time.sleep(0.1)
    # 进程的ID
    print("子进程ID >>",p.pid)
    # 进程在运行时为None、如果为–N,表示被信号N结束,了解即可
    print("进程的整数代码退出 >>",p.exitcode)
    # 进程的名称
    print("子进程的名称 >>",p.name)
    # 进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。
    # 这个键的用途是为涉及网络连接的底层进程间通信提供安全性,
    # 这类连接只有在具有相同的身份验证键时才能成功(了解即可)
    print("进程的身份验证键 >>",p.authkey)
    # 杀死进程,将任务提交给操作系统,会有点延迟
    p.terminate()
    time.sleep(0.1)
    # 判断p进程是否还存在
    print("进程是否存在:",p.is_alive())
    print("这里是父进程!")

结果是:
在这里插入图片描述
属性:authkey、daemon(要通过start()设置)、exitcode(进程在运行时为None、如果为–N,表示被信号N结束)、name、pid。其中daemon是父进程终止后自动终止,且自己不能产生新进程,必须在start()之前设置。

join()方法表示等待子进程结束以后再继续往下运行,通常用于进程间的同步。
注意:

在Windows上要想使用进程模块,就必须把有关进程的代码写在当前.py文件的if name == ‘main’ :语句的下面,才能正常使用Windows下的进程模块 ,代码实例如下:

from multiprocessing import Process
 
def func(i):
    print("这里是子进程%s!"%(i))
 
if __name__ == '__main__':
    p_s=[]
    for i in range(5):
        p = Process(target = func,args=(i+1,),name="子进程")
        p.start()
        p_s.append(p)
    [i.join() for i in p_s]
    print("这里是父进程!")
    # join([timeout]) timeout 等待子进程运行的时间,默认为一直等

输出结果是:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190821190335977.pn
守护进程

 from multiprocessing import Process
    import time
     
    def func():
        print("子进程开始执行")
        time.sleep(1)
        print("子进程结束执行")
     
    if __name__ == '__main__':
        print("主进程开始执行")
        p = Process(target=func)
        # 将p设置为守护进程,此代码一定要在start之前设置。
        # 守护进程会在父进程代码执行结束后就终止
        # 守护进程内无法再开启子进程,否则抛出异常
        p.daemon = True
        p.start()
        time.sleep(0.5)
        print("主进程结束执行")

输出结果:
在这里插入图片描述
主进程创建子进程目的是:主进程有一个任务需要并发执行,那开启子进程帮我并发执行任务
主进程创建子进程,然后将该进程设置成守护自己的进程
如果我们有两个任务需要并发执行,那么开一个主进程和一个子进程分别去执行就ok了,如果子进程的任务在主进程任务结束后,就没有存在的必要了,那么该子进程应该在开启前就被设置成守护进程。主进程代码运行结束,守护进程随即终止子进程还没来得及开始就挂了,设置了守护进程 就是不给子进程运行
守护进程必须在开启子进程前开启
进程锁
很多时候,我们需要在多个进程中同时写一个文件,如果不加锁机制,就会导致写文件错乱
这个时候,我们可以使用multiprocessing.Lock()

from multiprocessing import Process
from multiprocessing import Lock
from multiprocessing import Value
 
def func(num,lock):
    # 开启锁
    lock.acquire()
    for i in  range(100):
        num.value+=1
    # 释放锁
    lock.release()
 
if __name__ == '__main__':
    # Value()使线程共享内存
    num=Value("i",0)
    lock=Lock()
    p_s=\[\]
    for i in range(3):
        p = Process(target=func,args=(num,lock,))
        p.start()
        p_s.append(p)
    \[i.join() for i in p_s\]
    # 不加锁可能导致数据混乱,使最后结果不为300
    print(num.value)

输出如下:
在这里插入图片描述
进程池

Pool类可以提供指定数量的进程供用户调用,当有新的请求提交到Pool中时,如果池还没有满,就会创建一个新的进程来执行请求。如果池满,请求就会告知先等待,直到池中有进程结束,才会创建新的进程来执行这些请求。
实现一个多进程下的文件夹复制功能:

from multiprocessing import Pool
import os, time, random

def worker(msg):
    print("%s开始执行,进程号为%d"%(msg, os.getpid()))
    time.sleep(1)
    print "%s执行完毕"%(msg)

if __name__ == '__main__':
    po = Pool(3)  # 定义一个进程池,最大进程数3
    for i in range(10):
        # Pool.apply_async(要调用的目标,(传递给目标的参数元祖,))
        # 每次循环将会用空闲出来的子进程去调用目标
        po.apply_async(worker, (i,))

    print("----start----")
    po.close()  # 关闭进程池,关闭后po不再接收新的请求
    po.join()  # 等待po中所有子进程执行完成,必须放在close语句之后
    print("-----end-----")

输出如下:
在这里插入图片描述

apply():
函数原型:apply(func[, args=()[, kwds={}]])

该函数用于传递不定参数,主进程会被阻塞直到函数执行结束(不建议使用,并且3.x以后不再使用)。

map()
函数原型:map(func, iterable[, chunksize=None])

Pool类中的map方法,与内置的map函数用法行为基本一致,它会使进程阻塞直到返回结果。

注意,虽然第二个参数是一个迭代器,但在实际使用中,必须在整个队列都就绪后,程序才会运行子进程。

close()
关闭进程池(Pool),使其不再接受新的任务。

Close()要在join()之后使用

pool.close()#关闭进程池,不再接受新的任务
pool.join()#主进程阻塞等待子进程的退出

terminate()
立刻结束工作进程,不再处理未处理的任务。

join(),map(),close(),terminate(),apply()

join()
使主进程阻塞等待子进程的退出,join方法必须在close或terminate之后使用。
python进程间通信-Queue !!!

# 队列可以进程共享

import multiprocessing

queue = multiprocessing.Queue()
# 注意队列只能单向,要么是父进程插入子进程取出,要么是子进程插入父进程取出

def func(myq):
    myq.put([1, 2, 3, 4])  # 子进程插入


if __name__ == '__main__':
    # queue.put(['a','b'])  # 这里若是脚本父进程先插入了,子进程就没法再插入了
    p = multiprocessing.Process(target=func, args=(queue,))
    p.start()
    print(queue.get())  # 脚本父进程取出

输出如下:
在这里插入图片描述

(1) 在实例化Queue类,可以传递最大消息数,如q = Queue(5),这段代码是指只允许消息队列中最大有5个消息数据。如果不加最大消息数或数量为负值,则表达不限制数量直到占满内存;
(2) Queue.qsize():返回当前队列包含的消息数量;
(3) Queue.empty():如果队列为空,返回True,反之False ;
(4) Queue.full(): 如果队列满了,返回True,反之False;
(5) Queue.get([block[, timeout]]):获取队列中的一条消息,然后将其从列队中移除,block默认值为True;
1)如果block使用默认值,且没有设置timeout(单位秒),消息列队如果为空,此时程序将被阻塞(停在读取状态),直到从消息列队读到消息为止,如果设置了timeout,则会等待timeout秒,若还没读取到任何消息,则抛出”Queue.Empty”异常;
2)如果block值为False,消息列队如果为空,则会立刻抛出”Queue.Empty”异常;
(6) Queue.get_nowait():相当Queue.get(False);
(7) Queue.put(item,[block[, timeout]]): 将item消息写入队列,block默认值为True;
1)如果block使用默认值,且没有设置timeout(单位秒),消息列队如果已经没有空间可写入,此时程序将被阻塞(停在写入状态),直到从消息列队腾出空间为止,如果设置了timeout,则会等待timeout秒,若还没空间,则抛出”Queue.Full”异常;
2)如果block值为False,消息列队如果没有空间可写入,则会立刻抛出”Queue.Full”异常;
(8) Queue.put_nowait(item):相当Queue.put(item, False);
Python 并发编程多进程
进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的。
队列
可以往队列里放任意类型的数据
创建队列的类(底层就是以管道和锁定的方式实现):Queue([maxsize]):创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。
参数介绍:
maxsize是队列中允许最大项数,省略则无大小限制。
主要方法:
1 q.put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。
2 q.get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常.
3 q.get_nowait():同q.get(False)
4 q.put_nowait():同q.put(False)
5 q.empty():调用此方法时q为空则返回True,该结果不可靠,比如在返回True的过程中,如果队列中又加入了项目。
6 q.full():调用此方法时q已满则返回True,该结果不可靠,比如在返回True的过程中,如果队列中的项目被取走。
7 q.qsize():返回队列中目前项目的正确数量,结果也不可靠,理由同q.empty()和q.full()一样

# 队列可以进程共享

import multiprocessing

queue = multiprocessing.Queue()
# 注意队列只能单向,要么是父进程插入子进程取出,要么是子进程插入父进程取出

def func(myq):
    myq.put([1, 2, 3, 4])  # 子进程插入


if __name__ == '__main__':
    # queue.put(['a','b'])  # 这里若是脚本父进程先插入了,子进程就没法再插入了
    p = multiprocessing.Process(target=func, args=(queue,))
    p.start()
    print(queue.get())  # 脚本父进程取出

输出为:
在这里插入图片描述

管道pipe
pipe()返回两个连接对象代表pipe的两端。每个连接对象都有send()方法和recv()方法。
但是如果两个进程或线程对象同时读取或写入管道两端的数据时,管道中的数据有可能会损坏。
当进程使用的是管道两端的不同的数据则不会有数据损坏的风险。

    # 进程通信
from multiprocessing import Process, Pipe
def f(conn):  # conn表示管道类型
    conn.send([42, None, 'hello world']) # 发送的数据
    conn.close() # 关闭

if __name__ == '__main__':
    parent_conn, child_conn = Pipe()  # 创建一个管道,有两个口
    p = Process(target=f, args=(child_conn,))
    p.start()
    p.join()
    print(parent_conn.recv())   # prints "[42, None, 'hello']"
 # print(id(conn_a),id(conn_b))
 # print(type(conn_a), type(conn_b)) 
 # #multiprocessing.connection.Connection链接,意思就是连接口
# 相当于在进程中conn_a.send(['a','b','c','d','e']),发送给conn_b

输出为:
在这里插入图片描述
恍恍惚惚嘻嘻哈哈啦啦啦啦
懵逼的一天 今日总结如上

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值