day36 GIL锁与线程池

多进程与多线程效率对比

#
# """ # # 计算密集型 # """  # from threading import Thread # from multiprocessing import Process # import time # # a = 1 # def task(): # global a # for i in range(10000000): # a +=1 # a * 10 / 2 - 3 # # s = time.time() # #多线程 # # t1 = Thread(target=task) # # t2 = Thread(target=task) # # t3 = Thread(target=task) # # if __name__ == '__main__': # # # 多进程 # t1 = Process(target=task) # t2 = Process(target=task) # t3 = Process(target=task) # t1.start() # t2.start() # t3.start() # # t1.join() # t2.join() # t3.join() # # print(time.time() - s) # """ IO型任务 """ from threading import Thread from multiprocessing import Process import time def task(): # for i in range(10): with open(r"D:\脱产5期内容\day34\视频\1.线程理论.mp4",mode="rb")as f: while True: data = f.read(1024) if not data: break s = time.time() if __name__ == '__main__': # 多线程 t1 = Thread(target=task) t2 = Thread(target=task) t3 = Thread(target=task) # 多进程 # t1 = Process(target=task) # t2 = Process(target=task) # t3 = Process(target=task) t1.start() t2.start() t3.start() t1.join() t2.join() t3.join() print(time.time() - s)

GIL锁

什么是GIL
    全局解释器锁,是加在解释器上的互斥锁
    只存在于cpython解释器中
    
    python的内存回收管理机制,简称GC

GIL的加锁与解锁时机

加锁的时机:在调用解释器时立即加锁
解锁时机:
    当前进程遇到IO或超时

为什么需要GIL
    由于cpython的内存管理是非线程安全的,于是cpython就给解释器加了个锁,解决了安全问题,但是降低了效率,虽然有解决方  案,但是由于牵涉太多,一旦被修改,很多以前的基于GIL的程序都需要修改,所以变成了历史遗留问题


GIL带来的问题
    即使在多核处理器情况下,也无法真正的并行
    先有多线程模块,有这个问题,所以后来有了多进程模块弥补这个问题

总结:
    1.在单核情况下,无论是IO密集型还是计算密集,HIL都不会产生影响
    2.如果是多核下,IO密集型会受到GIl的影响,但是很明显IO速度比计算速度慢
    3.IO密集型多线程, 因为多线程开销小,节省资源,对于计算密集型,应该使用多进程,因为在cpytho中多线程是无法并行的

GIL与线程锁的区别

from threading import  Thread,Lock
import time

lock = Lock() a = 0 def task(): global a lock.acquire() temp = a time.sleep(0.01) a = temp + 1 lock.release() ts = [] for i in range(10): t1 = Thread(target=task) t1.start() ts.append(t1) for i in ts: i.join() print(a) GIL使用用于保护解释器相关的数据,解释器也是一段程序,肯定有其定义各种数据 GIL并不能保证自己定义的数据的安全,所有一旦 多核cpu中,进程可以并行,线程不能并行

多线程TCP

客户端
from threading import Thread
import socket

c = socket.socket()
c.connect(("127.0.0.1",8989))

def send_msg():
    while True:
        msg = input(">>>:") if not msg: continue c.send(msg.encode("utf-8")) send_t = Thread(target=send_msg) send_t.start() while True: try: data = c.recv(1024) print(data.decode("utf-8")) except: c.close() break 服务器端 from concurrent.futures import ThreadPoolExecutor from threading import Thread import socket  server = socket.socket() server.bind(("127.0.0.1",8989)) server.listen()  pool = ThreadPoolExecutor(3) def task(client): while True: try: data = client.recv(1024) if not data: client.close() break client.send(data.upper()) except Exception: client.close() break while True: client,addr = server.accept() # t = Thread(target=task,args=(client,)) # t.start() pool.submit(task,client)

线程池与进程池

池为容器,本质上就是一个存储进程或线程的列表

from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
from  threading import active_count,current_thread
import os,time
# 创建线程池 指定最大线程数为3  如果不指定 默认为CPU核心数 * 5
# pool = ThreadPoolExecutor(3)# 不会立即开启子线程 # # print(active_count()) # # def task(): # print("%s running.." % current_thread().name) # time.sleep(1) # # #提交任务到线程池 # for i in range(10): # pool.submit(task) #  # 创建进程池 最大进程数为3 默认为cpu个数 pool = ProcessPoolExecutor(3)# 不会立即开启子进程  # time.sleep(10) def task(): print("%s running.." % os.getpid()) time.sleep(1) if __name__ == '__main__': # #提交任务到进程池 for i in range(10): pool.submit(task) # 第一次提交任务时会创建进程 ,后续再提交任务,直接交给以及存在的进程来完成,如果没有空闲进程就等待  # 与信号量的区别 ,信号量也是一种锁 适用于保证同一时间能有多少个进程或线程访问 # 而线程/进程池,没有对数据访问进行限制仅仅是控制数量

同步与异步

"""
阻塞 非阻塞
程序遇到了IO操作,无法继续执行代码,叫做阻塞
程序没有遇到IO操作,正常执行中,就叫非阻塞
它们指的是程序的状态

    就绪  运行  阻塞

就绪和阻塞给人的感觉就是卡主了


同步 异步
同步(调用/执行/任务/提交),发起任务后必须等待任务结束,拿到一个结果才能继续运行
异步                     发起任务后不需要关系任务的执行过程,可以继续往下运行

异步效率高于同步
但是并不是所有任务都可以异步执行,判断一个任务是否可以异步的条件是,任务发起方是否立即需要执行结果


同步不等于阻塞  异步不等于非阻塞
当使用异步方式发起任务时 任务中可能包含io操作  异步也可能阻塞
同步提交任务 也会卡主程序 但是不等同阻塞,因为任务中可能在做一对计算任务,CPU没走
"""

# 使用线程池 来执行异步任务

from concurrent.futures import ThreadPoolExecutor import time pool = ThreadPoolExecutor() def task(i): time.sleep(1) print("sub thread run..") i += 100 return i fs = [] for i in range(10): f = pool.submit(task,i) # submit就是一异步的方式提交任务 # print(f) # print(f.result()) # result是阻塞的 会等到这任务执行完成才继续执行 ,会异步变成同步 fs.append(f) # 是一个阻塞函数,会等到池子中所有任务完成后继续执行 pool.shutdown(wait=True) # pool.submit(task,1) # 注意 在shutdown之后 就不能提交新任务了 for i in fs: print(i.result()) print("over")

使用线程池 来发起异步任务

阻塞函数
pool.shutdown()

同步不是阻塞,异步也不完全是非阻塞,也有可能阻塞

猜你喜欢

转载自www.cnblogs.com/shanau2/p/10214584.html