python多进程、多线程

一、介绍

函数输入若有多个参数,新建一个只有一个参数的函数,返回想调用的函数

def do(x,y):
	return x+y

def do1(attr):
	return do(attr[0],attr[1])

1、多线程、多进程选择

1.进程Process的创建远远大于线程Thread创建占用的资源
2.使用多进程往往是用来处理CPU密集型(科学计算)的需求,如果是IO密集型(文件读取,爬虫等)则可以使用多线程去处理
3.在Windows上要想使用进程模块,就必须把有关进程的代码写在if __name__ == '__main__' 内,否则在Windows下使用进程模块会产生异常。Unix/Linux下则不需要
4.线程比进程具有更高的性能,这是由于多个线程共享同一个进程的虚拟空间。线程共享的环境包括进程代码段、进程的公有数据等,利用这些共享的数据,线程之间很容易实现通信。
5.操作系统在创建进程时,必须为该进程分配独立的内存空间,并分配大量的相关资源,但创建线程则简单得多。因此,使用多线程来实现并发比使用多进程的性能要高得多。

2、multiprocessing模块介绍

1)管理进程模块
Process(用于创建进程模块)
Pool(用于创建管理进程池)
Queue(用于进程通信,资源共享)
Value,Array(用于进程通信,资源共享)
Pipe(用于管道通信)
Manager(用于资源共享)
2)同步子进程模块
Condition:把Condiftion理解为一把高级的琐,它提供了比Lock, RLock更高级的功能,允许我们能够控制复杂的线程同步问题
Event:一个简化版的 Condition。Event没有锁,无法使线程进入同步阻塞状态。
Lock
RLock
Semaphore:Semaphore 管理一个计数器,每调用一次 acquire() 方法,计数器就减一,每调用一次 release() 方法,计数器就加一。计时器的值默认为 1 ,计数器的值不能小于 0,当计数器的值为 0 时,调用 acquire() 的线程就会等待,直到 release() 被调用。 因此,可以利用这个特性来控制线程数量

3、创建进程例子

'''
    Process参数:
        group:进程所属组(基本不用) 
        target:表示调用对象
        args:表示调用对象的位置参数元组
        name:别名 
        kwargs:表示调用对象的字典
        
    Process属性/方法:
        属性:
            name:进程的名称。该名称是一个字符串,仅用于识别目的。
            daemon:标志是否为守护进程,默认为False
                    False : 主进程结束,子进程依然活跃
                    True : 主进程结束,子进程结束
            pid:进程ID
            exitcode:None表示进程尚未终止,0表示进程结束
        方法:
            run():表示进程运行的方法。可以在子类中重写此方法。
            start():启动进程,执行函数
            join():阻塞进程,参数timeout默认为None等待子进程结束,p.join(5)表示p进程最多阻塞5s
            is_alive():返回进程是否存活
'''
import multiprocessing
from multiprocessing import Process
import os

def do(name,age):
    print([name,age])
    return

#重写run方法
class myProcess(Process):

    def __init__(self,target,args,kwargs):
        super(myProcess, self).__init__()
        self.target = target
        self.args = args
        self.kwargs = kwargs

    #  调用start自动执行的函数
    def run(self):
        print(f'进程{self.name}的PID为{os.getpid()},父进程ID为 {os.getppid()}')
        self.target(*self.args,*self.kwargs)

if __name__ == '__main__':
     # multiprocessing.cpu_count()					 # 获取cpu的核数
     # multiprocessing.current_process().name        # 获取当前进程的名字
    
    p = Process(target=do, args=('tom',),kwargs={'age':18})      # ","表示元祖
    #设置为守护进程
    p.daemon = True
    # 启动进程,执行函数
    p.start()
    # 参数timeout默认为None等待子进程结束,p.join(5)表示p进程最多阻塞5s
    p.join()

    #调用重写run方法的进程类
    p = myProcess(target=do, args=('tom',), kwargs={'age': 18})
    p.start()

4、进程池例子

 '''  
    Pool参数:
        processes: 是要使用的工作进程数。如果进程是None,那么使用返回的数字os.cpu_count()。也就是说根据本地的cpu个数决定,processes小于等于本地的cpu个数;
        initialize: 初始化函数,如果initializer不是None,每一个进程在开始的时候会调用initializer(*initargs)。
        initargs: initializer的参数
        maxtasksperchild: 每个进程最大的任务量,如果你maxtasksperchild = 2, 那么干完两个任务后,就会创一个新的进程
        
    Pool方法:
        apply()
            函数原型:apply(func[, args=()[, kwds={}]])
            该函数用于传递不定参数,同python中的apply函数一致,主进程会被阻塞直到函数执行结束(不建议使用,并且3.x以后不再出现)
        
        apply_async()
            函数原型:apply_async(func[, args=()[, kwds={}[, callback=None]]])
            与apply用法一致,但它是非阻塞的且支持结果返回后进行回调
            回调函数的输入为输入函数的输出
        
        map()
            函数原型:map(func, iterable[, chunksize=None])
            Pool类中的map方法,与内置的map函数用法行为基本一致,它会使进程阻塞直到结果返回
            注意:虽然第二个参数是一个迭代器,但在实际使用中,必须在整个队列都就绪后,程序才会运行子进程
        
        map_async()
            函数原型:map_async(func, iterable[, chunksize[, callback]])
            与map用法一致,但是它是非阻塞的
            
        close()
            关闭进程池(pool),使其不再接受新的任务
        
        terminal()
            结束工作进程,不再处理未处理的任务
        
        join()
            主进程阻塞等待子进程的退出, join方法要在close或terminate之后使用
         
        imap()
        	返回迭代器形式的结果
 '''
 
import time
from multiprocessing import Pool
import multiprocessing


def do(n):
    # fn: 函数参数是数据列表的一个元素
    time.sleep(1)
    print(n * n)
    return n * n

def do1(n):
    print(n+1)
    return n+1

if __name__ == "__main__":

    testFL = [1, 2, 3, 4, 5, 6]
    pool = Pool(3)  # 创建拥有3个进程数量的进程池

    pool.apply_async(do, (3,))

    #进行回调
    pool.apply_async(func=do, args=(3,), callback=do1)  # do1的参数为do的返回值

    #多函数调用
    for func in [do,do1]:
        pool.apply_async(func,(10,))






    res = pool.map_async(do, testFL)
    '''
    map_async等同于:
        for i in testFL:
            pool.apply_async(do, (i,))
    '''
    # 也可以传函数参数
    # pool.map_async(do, (3,))

    # 得到执行函数结果,非阻塞执行,结果是和输入的顺序保持一致
    '''
    例如上面代码结果输出情况之一为:
        4
        1
        9
        16
        25
        36
        [1, 4, 9, 16, 25, 36]
    '''
    print(res.get())
    '''
    	#返回迭代器形式的结果
    	res = pool.imap(do,testFL)
    	time.sleep(3)
    	for i in res:
        	print(i)
    '''


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

5、进程通信(双向通信)

import multiprocessing


def func(conn):  # conn管道类型
    conn.send(["a", "b"])  # 发送的数据
    print("子进程:", conn.recv())  # 收到的数据
    conn.close()  # 关闭


if __name__ == "__main__":
    conn_a, conn_b = multiprocessing.Pipe()  # 创建一个管道,两个口
    p = multiprocessing.Process(target=func, args=(conn_a,))
    p.start()
    print("主进程:", conn_b.recv())
    conn_b.send([1, 2])
    p.join()
    conn_b.close()

6、共享变量

import multiprocessing
import time


def job(v, num):
    for _ in range(4):
        time.sleep(0.1)
        v.value += num   # v.value获取共享变量值
        print(v.value)

if __name__ == '__main__':
    '''
    多进程抢夺v,有可能会引起事务问题
    '''
    v = multiprocessing.Value('i', 0)  # 定义共享变量
    p1 = multiprocessing.Process(target=job, args=(v, 1))
    p2 = multiprocessing.Process(target=job, args=(v, 10))
    p1.start()
    p2.start()
    p1.join()
    p2.join()

共享变量类型参照下面两图映射关系:
在这里插入图片描述
在这里插入图片描述

7、进程同步

1)方法介绍
Lock和RLock都可以用来同步进程或者线程,它们之间的区别在于rlock是可重入的,也就是一个线程可以获取多次,只有在release相同次数时,RLock才会有locked状态转换为unlocked。
    '''
    	import threading
		lock = threading.Lock()
		#Lock对象
		lock.acquire()
		lock.acquire()
		#产生了死琐。(RLock则不会产生死锁)
		lock.release()
		lock.release()
    '''
    
Lock:
	Lock.acquire([blocking]):线程获取锁
            默认blocking时,调用线程会阻塞,直到锁的状态是unlocked,然后把锁设置为locked,返回True
            当设置blocking=False时,调用线程不会阻塞
            如果有多个线程因为获取锁而阻塞的话,当锁的状态是unloacked时,只有一个线程能继续执行
        Lock.release():
            释放锁
    
RLock:
	RLock.acquire([blocking]):
		线程阻塞或者非阻塞的获取锁
		不带参数调用 如果线程已经获的了锁,那么锁的引用加1,返回True;如果线程没有获得锁,线程阻塞直到锁的状态是unlocked. 如果这个锁没有被任何线程拥有过,设置锁的引用计数为1,然后返回True。
		设置blocking=False, 线程不阻塞
	Rlock.release():
		释放锁,每释放一个,锁的引用计数减1,直到锁的引用计数是0,锁的状态才设置为unlocked

Condition:
	把Condiftion理解为一把高级的琐,它提供了比Lock, RLock更高级的功能,允许我们能够控制复杂的线程同步问题
	acquire()/release():获得/释放 Lock
    wait([timeout]):线程挂起,直到收到一个notify通知或者超时(可选的,浮点数,单位是秒s)才会被唤醒继续运行。wait()必须在已获得Lock前提下才能调用,否则会触发RuntimeError。调用wait()会释放Lock,直至该线程被Notify()、NotifyAll()或者超时线程又重新获得Lock.
    notify(n=1):通知其他线程,那些挂起的线程接到这个通知之后会开始运行,默认是通知一个正等待该condition的线程,最多则唤醒n个等待的线程。notify()必须在已获得Lock前提下才能调用,否则会触发RuntimeError。notify()不会主动释放Lock。
    notifyAll(): 如果wait状态线程比较多,notifyAll的作用就是通知所有线程(这个一般用得少)

Event:
	set(): 将标志设为True,并通知所有处于等待阻塞状态的线程恢复运行状态。
	clear(): 将标志设为False。
	wait(timeout): 如果标志为True将立即返回,否则阻塞线程至等待阻塞状态,等待其他线程调用set()。
	isSet(): 获取内置标志状态,返回True或False。


死锁:
	当前线程拥有其他线程需要的资源
	当前线程等待其他线程已拥有的资源
	都不放弃自己拥有的资源
2)Lock/RLock例子
import multiprocessing
import time

def job(v, num,lock):
    lock.acquire()
    for _ in range(5):
        time.sleep(0.1)
        v.value += num  # v.value获取共享变量值
        print(v.value)
    lock.release()

if __name__ == '__main__':
	# lock = multiprocessing.Lock()
	lock = multiprocessing.RLock()
    v = multiprocessing.Value('i', 0)  # 定义共享变量
    p1 = multiprocessing.Process(target=job, args=(v, 1, lock))
    p2 = multiprocessing.Process(target=job, args=(v, 10, lock))  # 设定不同的number看如何抢夺内存
    p1.start()
    p2.start()
    p1.join()
    p2.join()
3)Condition例子
from multiprocessing import Process,Condition
import time

def Seeker(cond, name):
    #让Hider先执行
    time.sleep(1)
    cond.acquire()
    #唤醒Hider
    cond.notify()
    #等待被唤醒
    cond.wait()
    print('%s is going!'% name)
    cond.release()


def Hider(cond, name):
    cond.acquire()
    #等待被唤醒
    cond.wait()
    print('%s is going!' % name)
    # 唤醒Seeker
    cond.notify()
    cond.release()


if __name__ == '__main__':
    cond = Condition()
    seeker = Process(target=Seeker, args=(cond, 'seeker'))
    hider = Process(target=Hider, args=(cond, 'hider'))
    seeker.start()
    hider.start()

4)Event例子
from multiprocessing import Process,Event

def Seeker(event, name):
    #唤醒Hider
    event.set()
    #等待被唤醒
    event.wait()
    print('%s is going!'% name)



def Hider(event, name):
    #等待被唤醒
    event.wait()
    print('%s is going!' % name)
    # 唤醒Seeker
    event.set()


if __name__ == '__main__':
    '''
    因为没有锁,所以wait()后面的语句需要看资源情况和代码确定谁先跑完
    '''
    event = Event()
    seeker = Process(target=Seeker, args=(event, 'seeker'))
    hider = Process(target=Hider, args=(event, 'hider'))
    seeker.start()
    hider.start()
5)Semaphore例子
from multiprocessing import Process, Semaphore

def test(a,sem):
    print(a)
    # 释放 semaphore
    sem.release()

if __name__ == '__main__':

    # 设置计数器的值为 5
    sem = Semaphore(5)
    for i in range(10):
        # 获取一个 semaphore
        sem.acquire()
        t = Process(target=test, args=(i,sem))
        t.start()

8、threading模块介绍

多线程模块,使用方式和multiprocessing基本一致

9、创建线程例子

from threading import Thread

def do(name, age):
    print([name, age])
    return


# 重写run方法
class myThread(Thread):
    def __init__(self, target, args, kwargs):
        super(myThread, self).__init__()
        self.target = target
        self.args = args
        self.kwargs = kwargs

    #  调用start自动执行的函数
    def run(self):
        print(f'线程{self.name}')
        self.target(*self.args, *self.kwargs)


if __name__ == '__main__':

    t = Thread(target=do, args=('tom',), kwargs={'age': 18})  # ","表示元祖

    # 设置为守护线程
    # t.setDaemon(True)
    t.daemon = True
    # 启动进程,执行函数
    t.start()
    # 参数timeout默认为None等待子线程结束,p.join(5)表示p线程最多阻塞5s
    t.join()

    # 调用重写run方法的进程类
    t = myThread(target=do, args=('tom',), kwargs={'age': 18})
    t.start()

10、threading常用方法

threading.active_count():返回当前存活的线程对象的数量
threading.current_thread():返回当前线程对象
threading.enumerate():返回当前存在的所有线程对象的列表
threading.get_ident():返回线程pid
threading.main_thread():返回主线程对象

11、threading线程池

	'''
	pool调用方法:
    	submit(fn, *args, **kwargs):将 fn 函数提交给线程池。*args 代表传给 fn 函数的参数,*kwargs 代表以关键字参数的形式为 fn 函数传入参数。
    	map(func, *iterables, timeout=None, chunksize=1):该函数类似于全局函数 map(func, *iterables),只是该函数将会启动多个线程,以异步方式立即对 iterables 执行 map 处理。
    	shutdown(wait=True):关闭线程池。
    '''
    '''
    pool的返回值调用方法:
    	cancel():取消该 Future 代表的线程任务。如果该任务正在执行,不可取消,则该方法返回 False;否则,程序会取消该任务,并返回 True。
    	cancelled():返回 Future 代表的线程任务是否被成功取消。
    	running():如果该 Future 代表的线程任务正在执行、不可被取消,该方法返回 True。
    	done():如果该 Funture 代表的线程任务被成功取消或执行完成,则该方法返回 True。
    	result(timeout=None):获取该 Future 代表的线程任务最后返回的结果。如果 Future 代表的线程任务还未完成,该方法将会阻塞当前线程,其中 timeout 参数指定最多阻塞多少秒。
    	exception(timeout=None):获取该 Future 代表的线程任务所引发的异常。如果该任务成功完成,没有异常,则该方法返回 None。
    	add_done_callback(fn):为该 Future 代表的线程任务注册一个“回调函数”,当该任务成功完成时,程序会自动触发该 fn 函数。
    '''
 
from concurrent.futures import ThreadPoolExecutor
import time

def do(n):
    # fn: 函数参数是数据列表的一个元素
    time.sleep(1)
    # print(n * n)
    return n * n

def do1(n):
    n = n.result()
    print(n+1)
    return n+1

if __name__ == "__main__":
    testFL = [1, 2, 3, 4, 5, 6]
    pool = ThreadPoolExecutor(3)
    
    res = pool.submit(do, 3)
    print(res.result())
    
	# 获取异常信息,正常时为None
    if res._exception:
    	print(res._exception)
    # 正常时返回返回数据,异常时返回异常信息
    res.result()
    
    # do1的参数为res
    res.add_done_callback(do1)

    # res = pool.map(do, testFL)
    # for i in res:
    #     print(i)

    pool.shutdown()

12、线程同步

1)Lock/RLock例子
from threading import Thread,RLock
import time

def job(num,lock):
    global v
    lock.acquire()
    for _ in range(5):
        time.sleep(0.1)
        v += num
        print(v)
    lock.release()

if __name__ == '__main__':
    lock = RLock()

    v = 0
    p1 = Thread(target=job, args=(1, lock))
    p2 = Thread(target=job, args=(10, lock))  # 设定不同的number看如何抢夺内存
    p1.start()
    p2.start()
    p1.join()
    p2.join()
2)Condition例子
from threading import Thread,Condition
import time

def Seeker(cond, name):
    #让Hider先执行
    time.sleep(1)
    cond.acquire()

    #唤醒Hider
    cond.notify()
    #等待被唤醒
    cond.wait()
    print('%s is going!'% name)
    cond.release()


def Hider(cond, name):
    cond.acquire()

    #等待被唤醒
    cond.wait()
    print('%s is going!' % name)
    # 唤醒Seeker
    cond.notify()
    cond.release()


if __name__ == '__main__':
    cond = Condition()
    seeker = Thread(target=Seeker, args=(cond, 'seeker'))
    hider = Thread(target=Hider, args=(cond, 'hider'))
    seeker.start()
    hider.start()
3)Event例子
from threading import Thread,Event

def Seeker(event, name):
    #唤醒Hider
    event.set()
    #等待被唤醒
    event.wait()

    print('%s is going!'% name)

def Hider(event, name):
    #等待被唤醒
    event.wait()
    print('%s is going!' % name)
    # 唤醒Seeker
    event.set()


if __name__ == '__main__':
    '''
    因为没有锁,所以wait()后面的语句需要看资源情况和代码确定谁先跑完
    '''
    event = Event()
    seeker = Thread(target=Seeker, args=(event, 'seeker'))
    hider = Thread(target=Hider, args=(event, 'hider'))
    seeker.start()
    hider.start()
4)Semaphore例子
from threading import Thread,Semaphore

def test(a,sem):
    print(a)
    # 释放 semaphore
    sem.release()

if __name__ == '__main__':
    # 设置计数器的值为 5
    sem = Semaphore(5)
    for i in range(10):
        # 获取一个 semaphore
        sem.acquire()
        t = Thread(target=test, args=(i,sem))
        t.start()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值