进程、线程、以及协程:multiprocessing

multiprocessing模块就是跨平台版本的多进程管理包,支持子进程、通信和共享数据、执行不同形式的同步。该模块中有以下类/和方法:

#!/usr/bin/env python2.7
# -*- coding=utf-8 -*-

import multiprocessing
print dir(multiprocessing)


"""
['Array', 'AuthenticationError', 'BoundedSemaphore', 'BufferTooShort', 'Condition', 'Event', 'JoinableQueue', 'Lock', 'Manager', 'Pipe', 'Pool', 'Process', 'ProcessError', 'Queue', 'RLock', 'RawArray', 'RawValue', 'SUBDEBUG', 'SUBWARNING', 'Semaphore', 'TimeoutError', 'Value', '__all__', '__author__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', '__version__', '_multiprocessing', 'active_children', 'allow_connection_pickling', 'cpu_count', 'current_process', 'freeze_support', 'get_logger', 'log_to_stderr', 'os', 'process', 'sys', 'util']
"""

我们就拿着比较好玩的几个类去了解以下(后续如果用到了之后,到时候将这些内容进行分拆)

 

(进程)multiprocessing - Process

#!/usr/bin/env python2.7
# -*- coding=utf-8 -*-

from multiprocessing import Process
print dir(Process)

"""
['authkey', 'close', 'daemon', 'exitcode', 'ident', 'is_alive', 'join', 'kill', 'name', 'pid', 'run', 'sentinel', 'start', 'terminate']
"""

我们就先了解一下multiprocessing模块中的Process类(multiprocessing模块提供了Process类代表进程对象)

#!/usr/bin/env python2.7
# -*- coding=utf-8 -*-


from multiprocessing import Process
import os

def run_proc(name):
    print('Run child process is %s(%s)' %(name,os.getpid()))



if __name__ == '__main__':
    print('Parent process %s.' % os.getpid())
    p = Process(target=run_proc, args=('test',))
    print('Child process will start.')
    p.start()
    p.join()
    print('End')

"""
Parent process 18617.
Child process will start.
Run child process is test(18618)
End
"""

看了下Process类的源码(我们就捡着几个比较重要的地方说下) 

def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
    pass

Process参数(以下列的不是所有的参数,我们只是将常用到的罗列出来,如果以后遇到了需要用其他的参数,到时候再说)

target:表示调用对象,即该进程要执行的任务,或者说是传入一个方法;(看了其他博主的文章的时候这个地方如果默认不传递的话就会直接执行Process类的run方法)
args : srgs是一个元组,它里面是调用target方法需要传入的参数;
kwargs:kwargs是一个字典,它里面是调用target方法需要传入的关键字参数;

Process方法

start() : 启动进程,并调用方法run();
run() : 进程启动后运行target调用函数的方法;
join(timeout) :让主线程等待子进程结束后再继续进行,通常用于进程间的同步。timeout是可选的超时时间,即超过该时间主线程将不再继续等待。
terminate() :强制终止进程,但不会进行任何清理操作;
is_alive() :判断进程是否"存活",存活返回True,否则返回False;
 

看这些方法并不能给我们带来什么直观的感受,我们还是使用代码来一个个讲解

#!/usr/bin/env python2.7
# -*- coding=utf-8 -*-


from multiprocessing import Process
import os

def run_proc(name):
    print('Child Process %s(%s) is run' %(name, os.getpid()))


if __name__ == '__main__':
    print('Parent Process %s' %(os.getpid()))
    p = Process(target=run_proc, args=('test',))
    print('Child process will start.')
    p.start()
    print('End')


"""
第一个例子:主要是体现join方法的作用
Parent Process 18747
Child process will start.
End
Child Process test(18749) is run
"""
#!/usr/bin/env python2.7
# -*- coding=utf-8 -*-

from multiprocessing import Process
import os

def run_proc():
    print('Child Process (%s) is run' %(os.getpid()))

if __name__ == '__main__':
    print('Parent Process %s' %(os.getpid()))
    p = Process()
    p.run = run_proc
    print('Child process will start.')
    p.start()
    # 这里插播一个is_alive方法的例子
    p.is_alive()
    print('End')

"""
第二个例子:主要是体现run方法的作用
可以不指定子进程要运行的目标函数,到时候会执行Process类中的run方法的,我们这里将run方法替换成了我们自己写的,所以看起来又像是子进程执行了我们的内容一样
Parent Process 18789
Child process will start.
True
End
Child Process (18791) is run
"""

如果我们想要多进程运行该函数,这个时候我们可以这样写

#!/usr/bin/env python2.7
# -*- coding=utf-8 -*-


from multiprocessing import Process
import os

def run_proc(name):
    print('Child Process %s(%s) is run' %(name, os.getpid()))


if __name__ == '__main__':
    print('Parent Process is run (%s)' %(os.getpid()))
    p1 = Process(target=run_proc, args=('test1',))
    p2 = Process(target=run_proc, args=('test2',))
    p3 = Process(target=run_proc, args=('test3',))
    print('Child Process will be run')
    p1.start()
    p2.start()
    p3.start()
    p1.join()
    p2.join()
    p3.join()
    print('End')


"""
Parent Process is run (18967)
Child Process will be run
Child Process test1(18968) is run
Child Process test2(18969) is run
Child Process test3(18970) is run
End
"""

如果要启动大量的子进程,可以用进程池的方式批量创建子进程,就引出了我们下边要写的multiprocessing模块下的Pool类

 

(进程池)multiprocessing - Pool  reference (pool部分是跟着这个小哥的blog学习了,之前看其他的都没有弄懂)

还需要弄懂的是这里的阻塞 是阻塞下一个子进程执行 还是阻塞总体进程执行(mark)

from multiprocessing import Pool
p = Pool()
print(dir(p))

"""
以下是Pool对象的一些方法
['Process', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_cache', '_get_tasks', '_handle_results', '_handle_tasks', '_handle_workers', '_help_stuff_finish', '_initargs', '_initializer', '_inqueue', '_join_exited_workers', '_maintain_pool', '_maxtasksperchild', '_outqueue', '_pool', '_processes', '_quick_get', '_quick_put', '_repopulate_pool', '_result_handler', '_setup_queues', '_state', '_task_handler', '_taskqueue', '_terminate', '_terminate_pool', '_worker_handler', 'apply', 'apply_async', 'close', 'imap', 'imap_unordered', 'join', 'map', 'map_async', 'terminate']
"""


Pool类的构造方法:
def __init__(self, processes=None, initializer=None, initargs=(),maxtasksperchild=None):

下面介绍一下multiprocessing 模块下的Pool类下的几个方法:

  1、apply()【自我理解:多进程调用,单任务执行,同步且阻塞

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

    该函数用于传递不定参数,同python中的apply函数一致,主进程会被阻塞直到函数执行结束(不建议使用,并且3.x以后不在出现)。

#!/usr/bin/env python2.7
# -*- coding=utf-8 -*-


import multiprocessing
import time,os


def func(msg):
    print("msg : %s"%(msg))
    time.sleep(3)
    print("end : %s"%(msg))

if __name__ == "__main__":
    # 这里设置允许同时运行的%s"%(msg)的进程数量要考虑机器cpu的数量,进程的数量最好别小于cpu的数量,
    # 因为即使大于cpu的数量,增加了任务调度的时间,效率反而不能有效提高
    pool = multiprocessing.Pool(processes = 3)
    item_list = ['processes1' ,'processes2' ,'processes3' ,'processes4' ,'processes5' ,]
    count = len(item_list)
    for item in item_list:
        msg = "hello %s" %item
        # 维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去
        pool.apply(func, (msg,))

    # 注意执行输出的位置,这个是否就可以解释为阻塞的
    print('mark mark mark')
    pool.close()
    pool.join()  # 调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束
    print('Parent Process is run (%s)' %(os.getpid()))


"""
msg : hello processes1
end : hello processes1
msg : hello processes2
end : hello processes2
msg : hello processes3
end : hello processes3
msg : hello processes4
end : hello processes4
msg : hello processes5
end : hello processes5
mark mark mark
Parent Process is run (19331)

看执行结果应该能理解阻塞的(一个任务执行完再去执行下一个,这个感觉并不能将对应的进程效率利用起来)
"""

  2、apply_async【自我理解:多进程调用,单任务执行,异步且非阻塞

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

    与apply用法一致,但它是非阻塞的且支持结果返回后进行回调。

#!/usr/bin/env python2.7
# -*- coding=utf-8 -*-


import multiprocessing
import time,os


def func(msg):
    print("msg : %s"%(msg))
    time.sleep(3)
    print("end : %s"%(msg))

if __name__ == "__main__":
    # 这里设置允许同时运行的%s"%(msg)的进程数量要考虑机器cpu的数量,进程的数量最好别小于cpu的数量,
    # 因为即使大于cpu的数量,增加了任务调度的时间,效率反而不能有效提高
    pool = multiprocessing.Pool(processes = 3)
    item_list = ['processes1' ,'processes2' ,'processes3' ,'processes4' ,'processes5' ,]
    count = len(item_list)
    for item in item_list:
        msg = "hello %s" %item
        # 维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去
        pool.apply_async(func, (msg,))

    # 注意这里的执行输出的位置,这个是否可以解释为非阻塞的
    print('mark mark mark')
    pool.close()
    pool.join()  # 调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束
    print('Parent Process is run (%s)' %(os.getpid()))


"""
mark mark mark
msg : hello processes1
msg : hello processes2
msg : hello processes3
end : hello processes2
end : hello processes1
end : hello processes3
msg : hello processes4
msg : hello processes5
end : hello processes4
end : hello processes5
Parent Process is run (19346)
观察结果为非阻塞的,单次启动一个任务,但是是异步执行的,启动后不等待该任务是否结束开始新的任务

"""

  3、map() 【自我理解:多任务,阻塞】

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

    Pool类中的map方法,与内置的map函数用法行为基本一致,它会使进程阻塞直到结果返回。 
    注意:虽然第二个参数是一个迭代器,但在实际使用中,必须在整个队列都就绪后,程序才会运行子进程。

#!/usr/bin/env python2.7
# -*- coding=utf-8 -*-

from multiprocessing import Pool
import time


def forprint(num):
    time.sleep(num)
    print(num)
    return num ** 2



if __name__ == '__main__':

    start_time = time.time()
    p = Pool(2)
    list = [2, 3, 4]
    rlist = p.map(forprint, list)
    print rlist
    p.close()
    p.join()
    end_time = time.time()
    print "run time is %f"%(end_time-start_time)

"""
2
3
4
[4, 9, 16]
run time is 6.088700
解释:由于在进程池中创建了两个进程,所以代码会调用计算机的两个内核。而列表l中的三个元素中的前两个(“2”和“4”),会依次传入函数中,由计算机内核A和B去执行。当某一个内核执行完,会继续接收下一个传入参数“6”函数。而且内核A执行的函数,只会sleep两秒,所以,传入参数“6”的函数会由内核A去执行。所以A一共执行了2 + 6 为8秒,又由于是并行,所以总的执行时间是8秒(多的那零点几是初始化、赋值、打印等操作)
"""


if __name__ == '__main__':

    start_time = time.time()
    p = Pool(3)
    list = [2, 3, 4]
    rlist = p.map(forprint, list)
    print rlist
    p.close()
    p.join()
    end_time = time.time()
    print "run time is %f"%(end_time-start_time)

"""
2
3
4
[4, 9, 16]
run time is 4.104904
解释:在进程池中创建了三个进程,所以代码会调用计算机的三个内核 分配三个值进行计算 所以是并行执行,这个时候最多使用4秒
"""


"""
这个时候基本上可以看懂是啥意思了吧,上边是分配多任务的执行,没有解释阻塞 底下这个是解释阻塞
"""


import multiprocessing
import time
 
def func(msg):
    print("msg:", msg)
    time.sleep(2)
    print("end")
 
if __name__ == "__main__":
    pool = multiprocessing.Pool(2)
    pool.map(func, range(2))
 
    print("Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~")
    pool.close()
    pool.join()
    print("Sub-process(es) done.")
 
# 输出(注意Mark~位置):
msg: 0
msg: 1
end
end
Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~
Sub-process(es) done.

  4、map_async()

    函数原型:map_async(func, iterable[, chunksize[, callback]])
    与map用法一致,但是它是非阻塞的。其有关事项见apply_async。

import multiprocessing
import time
 
def func(msg):
    print("msg:", msg)
    time.sleep(2)
    print("end")
 
if __name__ == "__main__":
    pool = multiprocessing.Pool(2)
    pool.map_async(func, range(2))
 
    print("Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~")
    pool.close()
    pool.join()
    print("Sub-process(es) done.")
 
# 输出:
Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~
msg: 0
msg: 1
end
end
Sub-process(es) done.

  5、close()

    关闭进程池(pool),使其不在接受新的任务。

  6、join()

    主进程阻塞等待子进程的退出, join方法要在close或terminate之后使用。

       7、starmap (这个函数与map唯一的区别是可以传递多个参数)

import multiprocessing
import time
 
def func(msg1, msg2):
    print("msg1:", msg1, "msg2:", msg2)
    time.sleep(2)
    print("end")
 
if __name__ == "__main__":
    pool = multiprocessing.Pool(2)
    msgs = [(1,1),(2,2)]
    pool.starmap(func, msgs)
 
    print("Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~")
    pool.close()
    pool.join()
    print("Sub-process(es) done.")
 
# 输出
msg1: 1 msg2: 1
msg1: 2 msg2: 2
end
end
Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~
Sub-process(es) done.

      8starmap_async(非阻塞)

import multiprocessing
import time
 
def func(msg1, msg2):
    print("msg1:", msg1, "msg2:", msg2)
    time.sleep(2)
    print("end")
 
if __name__ == "__main__":
    pool = multiprocessing.Pool(2)
    msgs = [(1, 1), (2, 2)]
    pool.starmap_async(func, msgs)
 
    print("Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~")
    pool.close()
    pool.join()
    print("Sub-process(es) done.")
 
# 输出:
Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~
msg1: 1 msg2: 1
msg1: 2 msg2: 2
end
end
Sub-process(es) done.

 

(进程间通信)multiprocessing - (Queue and Pipe)

#!/usr/bin/python

#coding=utf-8

import os

from multiprocessing import Process, Pipe



def send(pipe):
    pipe.send(['spam'] + [42, 'egg'])
    pipe.close()



def talk(pipe):
    pipe.send(dict(name = 'Bob', spam = 42))
    reply = pipe.recv()
    print('talker got:', reply)



if __name__ == '__main__':
    (con1, con2) = Pipe()
    sender = Process(target = send, name = 'send', args = (con1, ))
    sender.start()
    print "con2 got: %s" % con2.recv()
    con2.close()
    (parentEnd, childEnd) = Pipe()
    child = Process(target = talk, name = 'talk', args = (childEnd,))
    child.start()
    print('parent got:', parentEnd.recv())
    parentEnd.send({x * 2 for x in 'spam'})
    child.join()
    print('parent exit')



输出如下:
con2 got: ['spam', 42, 'egg']
('parent got:', {'name': 'Bob', 'spam': 42})
('talker got:', set(['ss', 'aa', 'pp', 'mm']))
parent exit


虽然能大体知道这段代码在干什么,但是按照目前自己的代码量来说暂时想象不到对应的应用场景

getpid返回当前进程标识,getppid返回父进程标识。
from multiprocessing import Process, Queue
import os


def f(q):
    '''
    q: a Queue
    '''

    print('parent process:', os.getppid())
    print('process id:', os.getpid())

    # Add elements to the queue.
    # q.put([42, None, 'hello'])
    print q.get()


if __name__ == '__main__':
    q = Queue()
    # The subprocess here adds elements to the queue.
    p = Process(target=f, args=(q,))
    p.start()
    # Retreive the array from the queue.
    # print(q.get())  # prints "[42, None, 'hello']"
    q.put([43, None, 'hello'])
    p.join()


输出结果
('parent process:', 32636)
('process id:', 32637)
[43, None, 'hello']

(进程间同步)multiprocessing - (Lock and Rlock)

https://blog.csdn.net/weixin_44086593/article/details/87595895


# 这个例子没有锁
import multiprocessing as mp
import time

def job(v, num):

    for _ in range(10):
        time.sleep(0.1)
        v.value += num
        print(v.value)

def multicore():
    v = mp.Value('i', 0)
    p1 = mp.Process(target=job, args=(v, 1))
    p2 = mp.Process(target=job, args=(v, 3))
    p1.start()
    p2.start()
    p1.join()
    p2.join()


if __name__ == '__main__':
    multicore()

“”“
1
4
5
8
9
12
13
16
17
20
21
24
25
28
29
32
33
36
37
40
”“”

# 这个例子里面加了锁
import multiprocessing as mp
import time

def job(v, num, l): #传入锁
    l.acquire()  #锁住共享变量
    for _ in range(10):
        time.sleep(0.1)
        v.value += num
        print(v.value)
    l.release()  #释放共享变量

def multicore():
    l = mp.Lock() #定义锁
    v = mp.Value('i', 0)
    p1 = mp.Process(target=job, args=(v, 1, l))  #传入锁
    p2 = mp.Process(target=job, args=(v, 3, l))
    p1.start()
    p2.start()
    p1.join()
    p2.join()

if __name__ == '__main__':
    multicore()


“”“
1
2
3
4
5
6
7
8
9
10
13
16
19
22
25
28
31
34
37
40
”“”

 

 

名词解释:

同步

异步

阻塞

非阻塞

看了很多关于这块名词的解释 感觉都不是很理解(https://www.zhihu.com/question/19732473/answer/241673170 这个留着慢慢消化掉)

 

 

这次文章总结起来自己写了个💩 看来这个知识点不是一时半会能写的,所以暂时做一个留存了。等工作的时候用到了或者以后再去研究这一块的时候再去一块一块的分拆,进行学习

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值