Python 并发编程之GIL

                                  GIL

1.什么是 GIL:
在CPython 中,这个全局解释器锁,也称之为GIL,是一个互斥锁,防止多个线程在同一个时间执行python 字节码,这个锁是非常重要的,因为CPthon的内存管理非线程安全的,很多其他的特性依赖与GIL ,所以即使它影响了程序效率也无法将其直接去除

总结 :
在CPython中,GIL 会把线程的并行变成串行,导致效率降低

2.为什么需要GIL
GIL 与 GC (python中垃圾清理机制,当数据引用计数为0时,就会被清理)

GIL的加锁时机:
        在调用解释器时立即加锁


解锁时机:
1.当前线程遇到了io 时释放
2.当前线程执行时间超过设定值时释放,解释器会检测线程的执行时间,一旦到达某个阈值,通知线程保存状态切换线程,以此来保证数据安全
 
3.关于GIL的性能讨论
        优点:
                 保证了CPython 中的内存管理是线程安全的

       缺点:
                 互斥锁的特性使得多线程无法并行

  总结
1.单核下无论是io 密集还是计算密集GIL都不会产生任何影响
2.多核下对于io 密集任务,GIL会有细微的影响,基本可以忽略
3.Cpython 中io 密集任务应该采用多线程,计算密集型应该采用多进程

另外:之所以广泛采用cpthong解释器,就是因为大量的应用程序都是io 密集型的,还有另一个很重要的原因是 cpythong可以无缝对接各种c语言实现的库,这对于一些数学计算相关的应用程序而言非常的好,直接就能使用各种现成的算法

4.多线程与多进程效率对比

io 型任务

from threading import Thread
from multiprocessing import Process
import time

def task():
      with opean(r "D:\电影.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)


 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)

 
4.GIL 与自定义线程锁的区别
from threading import Thread,Lock
import time

lock = lock()
a = 0
def tadk():
      global a
      lock.acquire()
      temp = a
      time.sleep(0.01)
      a = temp+1
      lock.release()

ts = []
for i in range(10):
ti = Thread(target = task)
t1.start()
t2.append(t1)

for i in ts:
      i.join()

print(a)
    

总结
      GIL 使用 用于保护解释器相关的数据,解释器也是一段程序,肯定有其定义各种数据
      GIL 并不能保证你自己定义的数据安全,所以一旦你的程序出现了多线程共享数据时就需要自己加锁

 

from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor

from threading import active_count,current_thread
import os ,time

#创建线程池 指定最大线程数为3  如果不指定 默认cpu核心数*5
pool = ThreadPoolExecutor(3) #不会立即开启子线程
print (avtive_count())

def task():
     print("%s runing.."%current_thread().name)
     time.sleep(1)
if __name__ == '__main__':
for i in range(10):
      pool.submit(task)#提交任务到线程池 #第一次提交任务时会创建进程,后续再提交任务,直接交给以及存在的进程来完成,如果没有空闲进程就等待


#与信号量的区别,信号量也是一种锁 适用于保证同一时间能有多少个进程或线程访问
#而进程/进程池,没有对数据访问进行限制仅仅时控制数量

 多线程TCP服务器
from concurrent.futures import ThreadPoolExecutor
from threading import Thread
import socket

server = socket.socket()
server.bind(("127.0.001.002",6998))
server.listen()

pool = ThreadPoolExeutor(3)

def task(client):

      while True:

            try:

                data = client.recv(1024)

                 if not data:

                        ckient.close()

                         break

                 client.send(data.upper())

            except Exception:

                     client.close()

                          break

while True:

       client,adder = server.accept()

       pool.sumbit(task,client)

from threading import Thread
import socket
c = socket.socket()
c.conect(("127.001.0.001,6336))
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
 
阻塞 非阻塞
程序遇到了io 操作,无法继续执行代码,叫做阻塞
程序没有遇到io 操作,正常执行中,就叫非阻塞
它们指的是程序的状态
就绪和阻塞给人的感觉就是卡住了
同步 异步
同步(调用/执行/任务/提交),发起任务后必须等待任务结束,拿到一个结果才能继续运行
异步        (调用/执行/任务/提交),发起任务后不需要关心任务的执行过程,可以继续往下运行
异步效率 高于同步
但是并不是所有任务都可以异步执行,判断一个任务是否可以异步的条件是,任务发起方是否立即需要执行结果
同步不等于阻塞 异步不等于非阻塞
当使用异步方式发起任务时 任务中可能包含io 操作 异步也可能阻塞
同步提交任务 也会卡主程序 但是不等同阻塞,因为任务中可能做一对计算任务,CPU,没走

#使用线程池 来执行任务
from concurrent .futures import ThreadPoolExeutor
import time
pool = ThreadPoolExecutor()
def task(i):
       time.sleep(1)
       print ("sub thread run.")
       i += 100
       retrun 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")

           

   
 
 
 
 
 
 
 
 

猜你喜欢

转载自www.cnblogs.com/soulmateYANGYANG1225/p/10216201.html
今日推荐