一、介绍
函数输入若有多个参数,新建一个只有一个参数的函数,返回想调用的函数
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()