补充理解的知识点
- 并行:
- 多个cpu, 多个处理器或者是多核的处理器同时处理多个不同的任务。
- 并发:一个cpu, 一个处理器同时处理多个任务。
- 同步:一个任务执行完才能执行另一个任务。
- 异步:多个任务可以并发执行。不需要等待, 切换+保存状态。
进程
- 锁
-
lock.acquire()# 上锁
-
lock.release()# 解锁
-
同一时间允许一个进程上一把锁 就是Lock
-
加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,没错,速度是慢了,但牺牲速度却保证了数据安全。
-
同一时间允许多个进程上多把锁 就是[信号量Semaphore]
-
信号量是锁的变形: 实际实现是 计数器 + 锁,同时允许多个进程上锁
-
互斥锁Lock : 互斥锁就是进程的互相排斥,谁先抢到资源,谁就上锁改资源内容,为了保证数据的同步性
-
注意:多个锁一起上,不开锁,会造成死锁.上锁和解锁是一对.
-
- 死锁的问题解决用递归锁:
- 死锁, 加锁没开锁
- 递归锁: 作用就是解锁,RLock, 让noodles_lock和kuaizi_lock 都等于递归锁
- noodles_lock = kuaizi_lock = RLock()
def func(i):
print( "当前进程号:{} , 参数是{} ".format(os.getpid() , i) )
if __name__ == "__main__":
lst = []
startime = time.time()
for i in range(10000):
p = Process(target=func,args=(i,))
p.start()
lst.append(p)
# print(lst)
for i in lst:
i.join()
endtime = time.time()
print("运行的时间是{}".format(endtime - startime) ) # 运行的时间是101.68004035949707
多进程数据共享
- Manager
# ### Manager ( list 列表 , dict 字典 ) 进程之间共享数据
from multiprocessing import Process , Manager ,Lock
def mywork(data,lock):
# 共享字典
"""
lock.acquire()
data["count"] -= 10
lock.release()
"""
# 共享列表
data[0] += 1
if __name__ == "__main__":
lst = []
m = Manager()
lock = Lock()
# 多进程中的共享字典
# data = m.dict( {"count":5000} )
# print(data , type(data) )
# 多进程中的共享列表
data = m.list( [100,200,300] )
# print(data , type(data) )
""""""
# 进程数超过1000,处理该数据,死机(谨慎操作)
for i in range(10):
p = Process(target=mywork,args=(data,lock))
p.start()
lst.append(p)
# 必须等待子进程所有计算完毕之后,再去打印该字典,否则报错;
for i in lst:
i.join()
print(data)
- quque
from multiprocessing import Process, Queue
def test1(q):
print(q.get())
# print(q)
# q.put(124)
# q.put(test)
q.put(123)
return 123
# tt = test()
def test():
return 123
if __name__ == '__main__':
q = Queue()
p1 = Process(target=test1, args=(q,))
p1.start()
q.put(test)
p1.join()
print(q.get())
# q.get()
线程
def func(i):
print( "当前进程号:{} , 参数是{} ".format(os.getpid() , i) )
if __name__ == "__main__":
lst = []
startime = time.time()
for i in range(10000):
t = Thread(target=func,args=(i,))
t.start()
lst.append(t)
# print(lst)
for i in lst:
i.join()
endtime = time.time()
print("运行的时间是{}".format(endtime - startime) ) # 运行的时间是1.8805944919586182
- 线程间的数据安全,操作同一个数据的时候加上线程锁
from threading import Thread, Lock, Semaphore
import time
n = 0
def func1(lock):
global n
lock.acquire()
for i in range(1000000):
n += 1
lock.release()
def func2(lock):
global n
with lock:
for i in range(1000000):
n -= 1
if __name__ == '__main__':
lock = Lock()
lis = []
t1 = Thread(target=func1, args=(lock, ))
t1.start()
t2 = Thread(target=func2, args=(lock, ))
t2.start()
lis.append(t1)
lis.append(t2)
for i in lis:
i.join()
print('主进程结束,打印n:{0}'.format(n))
=====》
结果: 是0 ,不加锁会出错
进程池和线程池
from concurrent.futures import ProcessPoolExecutor , ThreadPoolExecutor
"""多条进程提前开辟,可触发多cpu的并行效果"""
# (1) 进程池 ProcessPoolExecutor
def func(i, j):
# print(i)
time.sleep(random.uniform(0.1,0.8))
print(" 任务执行中 ... start ... 进程号{}".format(os.getpid()) , i )
print(" 任务执行中 ... end ... 进程号{}".format(os.getpid()))
return i
if __name__ == "__main__":
lst = []
# (1) 创建进程池对象
"""4核电脑,进程默认是4, 线程是4*5"""
p = ProcessPoolExecutor()
# (2) 异步提交任务
"""submit(任务,参数1,参数2 ... )"""
"""默认如果一个进程短时间内可以完成更多的任务,进程池就不会使用更多的进程来辅助完成 , 可以节省系统资源的损耗;"""
for i in range(10):
obj = p.submit( func , i , 1)
# print(obj)
# print(obj.result()) 不要写在这,导致程序同步,内部有阻塞
lst.append(obj)
# (3) 获取当前任务的返回值
for i in lst:
print(i.result(),">===获取返回值===?")
# (4) shutdown 等待所有进程池里的进程执行完毕之后,在放行
p.shutdown()
print("进程池结束 ... ")
- 进程和线程池里的map
# (3) 线程池 map
from threading import currentThread as ct
from collections import Iterator,Iterable
def func(i):
time.sleep(random.uniform(0.1,0.7))
print("thread ... 线程号{}".format(ct().ident),i)
return "*" * i
if __name__ == "__main__":
t = ThreadPoolExecutor()
it = t.map(func,range(100))
# 返回的数据是迭代器
print(isinstance(it,Iterator))
# 协调子父线程,等待线程池中所有线程执行完毕之后,在放行;
t.shutdown()
# 获取迭代器里面的返回值
for i in it:
print(i)
协程
-
协程也叫纤程: 协程是线程的一种实现方式.
- 指的是一条线程能够在多任务之间来回切换的一种实现.
- 对于CPU、操作系统来说,协程并不存在.任务之间的切换会花费时间.
- 目前电脑配置一般线程开到200会阻塞卡顿.
-
协程的实现
- 协程帮助你记住哪个任务执行到哪个位置上了,并且实现安全的切换
- 一个任务一旦阻塞卡顿,立刻切换到另一个任务继续执行,保证线程总是忙碌的,更加充分的利用CPU,抢占更多的时间片
-
一个线程可以由多个协程来实现,协程之间不会产生数据安全问题
-
协程模块:gevent
“”“
#(1)apawm(函数,参数1,参数2,参数.…)启动协程
#(2)join阻塞,直到某个协程在任务执行完毕之后在放行
#(3)joinal1等待所有协程任务执行完毕之后放行;g1.join()g2.join()<=>gevent.joinall([gl,g2..])
#(4)value获取协程任务中的返回值gl.value g2.value rttrn from gevent import monkey;monkey.patch_all()
”“”
import gevent
import time
def eat():
print("eat1开始吃…")
time.sleep(1)
print("eat2继续吃..")
return "吃完了"
def play():
print("play1开始玩…")
time.sleep(1)
print("play2继续玩…")
return "玩完了"
# 创建协程对象g1
gl = gevent.spawn(eat)
# 创建协程对象g2
g2 = gevent.spawn(play)
# 等待所有协程任务执行完毕之后放行
gevent.joinall([gl, g2])
print("主线程执行结束.…")
# 获取协程任务中的返回值
print(g1.value)
print(g2.value)