前言

主要总结multiprocesssing 这个库的使用,其他多线程库感觉不好用……

进程池(pool)

相对于自己手写,还是这个进程池最好用,完全不用担心调度问题。

Pool 可以提供指定数量的进程供用户使用,默认是 CPU 核数。当有新的请求提交到 Poll 的时候,如果池子没有满,会创建一个进程来执行,否则就会让该请求等待。

  • Pool 对象调用 join 方法会等待所有的子进程执行完毕
  • 调用 join 方法之前,必须调用 close
  • 调用 close 之后就不能继续添加新的 Process 了

pool.apply_async

apply_async 方法用来同步执行线程,语序多个进程同时进入进程池

import multiprocessing
import os
import time

def run_task(name):
    print('Task {0} pid {1} is running'.format(name, os.getpid()))
    time.sleep(1)
    print('Task {0} end.'.format(name))

if __name__ == '__main__':
    print('current process {0}'.format(os.getpid()))
    p = multiprocessing.Pool(processes=3)  #设置进程池并发最大数量
    for i in range(6):
        p.apply_async(run_task, args=(i,))
    # for循环改为这个一样的效果      p.map(run_task,[i for i in range(6)])
    print('Waiting for all subprocesses done...')
    p.close() #关闭进程池,不允许新进程进入
    p.join()  #主进程阻塞,等待其他进程执行完成
    print('All processes done!')  #主进程结束

运行结果:

C:\Users\Archerx\AppData\Local\Programs\Python\Python36\python.exe F:/code/multi/test1.py
current process 13632
Waiting for all subprocesses done...
Task 0 pid 6788 is running
Task 1 pid 14172 is running
Task 2 pid 6872 is running
Task 0 end.
Task 1 end.
Task 4 pid 14172 is running
Task 2 end.
Task 3 pid 6788 is running
Task 5 pid 6872 is running
Task 3 end.
Task 4 end.
Task 5 end.
All processes done!

Process finished with exit code 0

pool.apply

跟单进程执行到底多了进程调度时间

import multiprocessing
import os
import time

def run_task(name):
    print('Task {0} pid {1} is running'.format(name, os.getpid()))
    time.sleep(1)
    print('Task {0} end.'.format(name))

if __name__ == '__main__':
    print('current process {0}'.format(os.getpid()))
    p = multiprocessing.Pool(processes=3)  #设置进程池并发最大数量
    for i in range(6):
        p.apply(run_task, args=(i,))
    print('Waiting for all subprocesses done...')
    p.close() #关闭进程池,不允许新进程进入
    p.join()  #主进程阻塞,等待其他进程执行完成
    print('All processes done!')  #主进程结束

执行结果:

current process 12800
Task 0 pid 12172 is running
Task 0 end.
Task 1 pid 10060 is running
Task 1 end.
Task 2 pid 14540 is running
Task 2 end.
Task 3 pid 12172 is running
Task 3 end.
Task 4 pid 10060 is running
Task 4 end.
Task 5 pid 14540 is running
Task 5 end.
Waiting for all subprocesses done...
All processes done!

process类

除了pool那个很常用,但是你对自定义要求程度很高,那pool可能就满足不了你,只有用这个了

Process 类用来描述一个进程对象。创建子进程的时候,只需要传入一个执行函数和函数的参数即可完成 Process 示例的创建。

  • star() 方法启动进程,
  • join() 方法实现进程间的同步,等待所有进程退出。
  • close() 用来阻止多余的进程涌入进程池 Pool 造成进程阻塞。
multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
  • target 是函数名字,需要调用的函数
  • args 函数需要的参数,以 tuple 的形式传入
    示例:
import multiprocessing
import os

def run_proc(name):
    print('Child process {0} {1} Running '.format(name, os.getpid()))

if __name__ == '__main__':
    print('Parent process {0} is Running'.format(os.getpid()))
    for i in range(5):
        p = multiprocessing.Process(target=run_proc, args=(str(i),))
        print('process start')
        p.start()
    p.join()
    print('Process close')
    
运行结果:
Parent process 809 is Running
process start
process start
process start
process start
process start
Child process 0 810 Running 
Child process 1 811 Running 
Child process 2 812 Running 
Child process 3 813 Running 
Child process 4 814 Running 
Process close

多线程

在一个进程下开启多个线程与在一个进程下开启多个子进程的区别

别人博客,看到一个很有意思的比较

开启速度,主进程下开启线程速度较快。

from threading import Thread
from multiprocessing import Process
import os

def work():
    print('hello')

if __name__ == '__main__':
    #在主进程下开启线程
    t=Thread(target=work)
    t.start()
    print('主进程-->线程')
    #在主进程下开启子进程
    t=Process(target=work)
    t.start()
    print('主进程-->子进程')

运行结果:
hello
主进程-->线程
主进程-->子进程
hello

进程与线程PID问题

在主进程下开启多个线程,每个线程都跟主进程的pid一样,开多个进程,每个进程都有不同的pid

from threading import Thread
from multiprocessing import Process
import os

def work():
    print('hello',os.getpid())

if __name__ == '__main__':
    #part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样
    t1=Thread(target=work)
    t2=Thread(target=work)
    t1.start()
    t2.start()
    print('主进程-->线程pid',os.getpid())

    #part2:开多个进程,每个进程都有不同的pid
    p1=Process(target=work)
    p2=Process(target=work)
    p1.start()
    p2.start()
    print('主进程-->子进程pid',os.getpid())

运行结果:
hello 13002
hello 13002
主进程-->线程pid 13002
主进程-->子进程pid 13002
hello 13003
hello 13004

线程的阻塞

与进程的方法都是类似的,其实是multiprocessing模仿threading的接口

from threading import Thread
import time
def sayhi(name):
    time.sleep(2)
    print('%s say hello' %name)

if __name__ == '__main__':
    t=Thread(target=sayhi,args=('h',))
    t.start()
    t.join()
    print('主线程')
    print(t.is_alive())

线程其他相关方法

Thread实例对象的方法

  • isAlive(): 返回线程是否活动的。

    • getName(): 返回线程名。
    • setName(): 设置线程名。

threading模块提供的一些方法:

  • threading.currentThread(): 返回当前的线程变量。
  • threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
  • threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

线程死锁、线程同步、线程通信暂时用不到就不整理了

preView