python之并发编程:多进程与多线程

一:多进程

       进程(process),是计算机中已运行程序的实体,是线程的容器;一个进程至少有一个线程

1,父进程与其子进程:
       各进程的内存空间是互相隔离的
       进程创建时,为该进程生成一个PCB(进程控制块);进程终止时,回收PCB。每个进程都有一个非负的唯一进程ID(PID)。虽然是唯一的,但是PID可以重用,当一个进程终止后,其他进程就可以使用它的PID了。

       linux:调用fork创建子进程
       PID为0的进程为调度进程,该进程是内核的一部分,也称为系统进程;PID为1的进程为init进程,它是一个普通的用户进程,但是以超级用户特权运行;PID为2的进程是页守护进程,负责支持虚拟存储系统的分页操作。  
       子进程刚开始,内核并没有为它分配物理内存,而是以只读的方式共享父进程内存,只有当子进程写时,才分配物理内存,复制父进程的数据给子进程,即“copy-on-write”

       windows:调用CreateProcess创建子进程
       子进程创建时就为它分配物理内存,复制父进程的数据给子进程

       僵尸进程:子进程结束了,父进程还未结束,此时,子进程的一些资源(如PID)还未回收,需要等到父进程结束才能回收,就称这样的子进程为僵尸进程

       孤儿进程(linux):父进程结束了,子进程还在,此时,这些子进程托管给init进程来管理

2,创建子进程的两种方式:

from multiprocessing import Process
import os
# multiprocessing 是一个包,threading是一个模块,它俩有着相似的API

#方式一:自定义创建子进程的类,继承于Process类
class Myprocess(Process):
    def __init__(self,runner):
        super().__init__()
        self.runner = runner
        self.name = '子进程'         # 自定义子进程的名字,默认为Myprocess-1,Myprocess-2
    def run(self):     # 子进程的任务函数,自动执行,名字必须为run
        print('%s is running' % self.runner)
        print(self.name)
        print('PID为:%s,其父进程PID为:%s' %(os.getpid(),os.getppid()))

if __name__ == '__main__':
    subprocess_obj1 = Myprocess('rock')
    subprocess_obj2 = Myprocess('mike')
    subprocess_obj1.start()
    subprocess_obj2.start()

# 方式二:使用Process类
def task(name):
    print('%s is running' %name)
    print('PID为:%s,其父进程PID为:%s' %(os.getpid(),os.getppid()))

if __name__ == '__main__':
    # 必须写在 if __name__ == '__main__': 里面
    subprocess_obj = Process(target=task,args=('rock',),kwargs={},name='自定义进程名字')
    subprocess_obj.start()    # 向操作系统申请开启该子进程

    subprocess_obj.daemon = True  # 把该子进程对象设置为守护进程
    # 守护进程--被守护的进程代码一执行完守护进程就结束

    print(subprocess_obj.name)   # 取得该子进程对象的名字
    print(subprocess_obj.pid)    # 取得该子进程对象的PID
    subprocess_obj.join()        # 进入阻塞状态,等待该子进程执行结束
    subprocess_obj.is_alive()    # 判断该子进程对象是否仍存活
    subprocess_obj.terminate()   # 向操作系统申请结束该子进程
    print('嘿,我是父进程,我的PID为%s' %os.getpid())

3,Queue 与 JoinableQueue

       IPC机制:inter-process communication 进程间通信机制
       主要有管道,队列等。。。
       Queue 与 JoinableQueue:

       共享的内存空间;有互斥锁机制,以保障共享数据的安全

from multiprocessing import Queue,JoinableQueue
# Queue
# Returns a process shared queue implemented using a pipe and a few locks/semaphores
q = Queue(5)   # 队列对象,参数5表示队列中能存的值的个数
q.put('abc')   # 往队列里面存值;当队列满了时,这行语句执行被阻塞
q.get()        # 从队列里面取值;当队列为空时,这行语句执行被阻塞

# JoinableQueue
# JoinableQueue, a Queue subclass, is a queue which additionally has task_done() and join() methods.
q1 = JoinableQueue()
q1.put('abc')
q1.get()
# task_done()用于与join()搭配使用:
q1.task_done()   # 队列每被取走一个值就通知一次队列,通过这种方式能知道队列的值是否被取完
q1.join()        # 处于阻塞状态,等待队列所有值被取走


二:多线程

       线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。

1,进程与其线程:

       一个进程下的所有线程共享该进程的全部资源

2,创建子线程的两种方式:

from threading import Thread,current_thread
#方式一:自定义创建子线程的类,继承于Thread类
class Mythread(Thread):
    def __init__(self,runner):
        super().__init__()
        self.runner = runner
        # self.name = '子线程'         # 自定义子线程的名字,默认为Thread-1,Thread-2
    def run(self):    # 子线程的任务函数,自动执行,名字必须为run
        print('%s is running' % self.runner)
        print('my name is %s' %current_thread().name)

if __name__ == '__main__':
    subthread_obj1 = Mythread('rock')
    subthread_obj2 = Mythread('mike')
    subthread_obj1.start()
    subthread_obj2.start()

# 方式二:使用Process类
def task(name):
    print('%s is running' %name)
    print('my name is %s' %current_thread().name)   # Thread-1

if __name__ == '__main__':
    subthread_obj = Thread(target=task,args=('rock',))
    subthread_obj.start()    # 向操作系统申请开启该子线程

    print(subthread_obj.getName())
    print(subthread_obj.name)     # 取得该子线程对象的名字
    subthread_obj.daemon = True   # 设置该子线程对象为守护线程
    # 守护线程--被守护的线程结束(代码执行完不一定马上就结束)守护线程才结束

    subthread_obj.join()        # 进入阻塞状态,等待该子线程执行结束
    subthread_obj.is_alive()    # 判断该子线程对象是否仍存活
    print('嘿,我是主线程,我的名字是%s' %current_thread().name)   # MainThread

3,线程队列 queue

import queue
# The queue module implements multi-producer, multi-consumer queues. It is especially useful
# in threaded programming when information must be exchanged safely between multiple threads.

q = queue.Queue()  # 普通队列  先进先出
q.put(1)
q.get()

# task_done()用于与join()搭配使用:
q.task_done()   # 队列每被取走一个值就通知一次队列,通过这种方式能知道队列的值是否被取完
q.join()  # 处于阻塞状态,等待队列所有值被取走

q1 = queue.LifoQueue()  # 栈队列  先进后出
q1.put('a')
q1.get()
q1.task_done()
q1.join()

q2 = queue.PriorityQueue()  # 优先级队列,优先级最高的先出来
q2.put((1,'abc'))    # 参数格式:(priority_number, data)  数字越小优先级越高
q2.get()
q2.task_done()
q2.join()


三:三种锁的介绍

# 1,互斥锁
from multiprocessing import Lock
# from threading import Lock

lock = Lock()  # 创建互斥锁对象,该对象有两种状态:locked 和 unlocked ;初始状态为 unlocked

def task(lock):
    lock.acquire()   # acquire(block=True, timeout=None)
    # 实现原理:
    # 当锁对象是unlocked状态时,该函数调用正常,并立即将锁对象的状态转为locked状态;
    # 当锁对象是locked状态时,该函数调用被阻塞
    pass
    lock.release() # 把锁对象的状态由locked 转为 unlocked。若锁对象已经是unlocked状态时,会报错

# 2,递归锁
# from multiprocessing import RLock
from threading import RLock

r_lock = RLock()  # 这个类实现了可重入的锁对象,其初始递归级别为0

r_lock.acquire()
# 某线程第一次调用该函数时:
# 当锁对象的递归级别为0时,该函数调用正常,且立即将递归级别加1;其后该线程可多次调用该函数,每次将递归级别加1
# 当锁对象的递归级别不为0时,阻塞

r_lock.release()
# 将锁对象的递归级别减1

# 3,信号量
# from multiprocessing import Semaphore
from threading import Semaphore

s_lock = Semaphore(5)  # 产生信号量对象,内部计数器初始值为5;不传值则默认值为1

s_lock.acquire()
# Acquire a semaphore
# 当内部计数器值大于0时,该函数调用正常,且内部计数器立即执行减1操作;
# 当内部计数器值为0时,该函数调用被阻塞,直到内部计数器大于0时,恢复调用正常,内部计数器减1

s_lock.release()
# Release a semaphore
# 内部计数器立即执行加1操作
# 各种锁可以使用with声明语句,语法如下:
with some_lock:
    # do something...

# 其等同于:
some_lock.acquire()
try:
    # do something...
finally:
    some_lock.release()


四:进程池与线程池

       进程池:装子进程的池子,能控制子进程的个数,是一种保护机制,确保进程个数在机器性能可承受范围内

       线程池:装子线程的池子,能控制子线程的个数,是一种保护机制,确保线程个数在机器性能可承受范围内

from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor

# ProcessPoolExecutor
# An Executor subclass that executes calls asynchronously using a pool of at most max_workers processes.

# ThreadPoolExecutor
# An Executor subclass that uses a pool of at most max_workers threads to execute calls asynchronously
def task(n):
    n +=1
    return n

def fn(future):
    res = future.result()
    pass

executor = ThreadPoolExecutor(5) # 可创建的子线程数量最大为5,不传参数则默认为机器的cpu数*5
future = executor.submit(task,3)
# submit(fn, *args, **kwargs)  给线程池中的线程提交任务fn,即执行fn(*args, **kwargs);
# 返回一个表示可被调用的执行的Future对象
future.result()  # 取得任务执行完毕的返回值

future.add_done_callback(fn)  # 回调函数
# 把可调函数fn绑定给future对象;当future被取消或执行完毕时,fn会被调用(future作为fn的唯一参数)

executor.shutdown()
# 等当前所有挂起的future对象执行完再回收所有的资源

# You can avoid having to call this method explicitly if you use the with statement,
# which will shutdown the Executor
with ThreadPoolExecutor(5) as e:
    e.submit(task,3)
    e.submit(task,4)

# 进程池与线程池的使用方式基本相同;
# 不同点:进程池,父进程执行回调函数绑定的函数fn(因为future是父进程的变量);
# 线程池,某个线程继续执行函数fn
if __name__ == '__main__':
    p = ProcessPoolExecutor(5) # 可创建的子进程数量最大为5,不传参数则默认为机器的cpu数

    future = p.submit(task,4)
    print(future.result())  #Return the value returned by the call.

猜你喜欢

转载自blog.csdn.net/hua1011161696/article/details/80137605