zoukankan      html  css  js  c++  java
  • Python开发基础---多线程锁机制

    GIL(全局解释器锁)

    GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念,是为了实现不同线程对共享资源访问的互斥,才引入了GIL

    在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势

    python对于计算密集型的任务开多线程的效率甚至不如串行(没有大量切换),但是,对于IO密集型的任务效率还是有显著提升的。

    GIL原理图

     

    计算密集型:结果肯定是100,因为每一次start结果就已经出来了,所以第二个线程肯定是通过调用第一个线程的count值进行计算的

    复制代码
     1 def sub():
     2     global count
     3 
     4     '''线程的公共数据  下'''
     5     temp=count
     6     count=temp+1
     7     '''线程的公共数据  上'''
     8 
     9     time.sleep(2)
    10 count=0
    11 
    12 l=[]
    13 for i in range(100):
    14     t=threading.Thread(target=sub,args=())
    15     t.start()  #每一次线程激活,申请一次gillock
    16     l.append(t)
    17 for t in l:
    18     t.join()
    19 print(count)
    复制代码

    io密集型:当第一个线程开始start的时候,由于sleep了0.001秒,这0.001秒对于人而言很短,但是对于cpu而言,这0.001秒已经做了很多的事情了,在这里cpu做的事情就是或许已经start了100个线程,所以导致大多数的线程调用的count值还是0,即temp=0,只有少数的线程完成了count=temp+1的操作,所以输出的count结果不确定,可能是7、8、9,也可能是10几。

    复制代码
     1 def sub():
     2     global count
     3 
     4     '''线程的公共数据  下'''
     5     temp=count
     6     time.sleep(0.001)    #大量的io操作
     7     count=temp+1
     8     '''线程的公共数据  上'''
     9 
    10     time.sleep(2)
    11 count=0
    12 
    13 l=[]
    14 for i in range(100):
    15     t=threading.Thread(target=sub,args=())
    16     t.start()
    17     l.append(t)
    18 for t in l:
    19     t.join()
    20 print(count)
    复制代码

    注意以下的锁都是多线程提供的锁机制,与python解释器引入的gil概念无关

    互斥锁(同步锁)

    互斥锁是用来解决上述的io密集型场景产生的计算错误,即目的是为了保护共享的数据,同一时间只能有一个线程来修改共享的数据。

    复制代码
     1 def sub():
     2     global count
     3     lock.acquire()  #上锁,第一个线程如果申请到锁,会在执行公共数据的过程中持续阻塞后续线程
     4                     #即后续第二个或其他线程依次来了发现已经被上锁,只能等待第一个线程释放锁
     5                     #当第一个线程将锁释放,后续的线程会进行争抢
     6 
     7     '''线程的公共数据  下'''
     8     temp=count
     9     time.sleep(0.001)
    10     count=temp+1
    11     '''线程的公共数据  上'''
    12 
    13     lock.release()  #释放锁
    14     time.sleep(2)
    15 count=0
    16 
    17 l=[]
    18 lock=threading.Lock()   #将锁内的代码串行化
    19 for i in range(100):
    20     t=threading.Thread(target=sub,args=())
    21     t.start()
    22     l.append(t)
    23 for t in l:
    24     t.join()
    25 print(count)
    复制代码

    死锁

    保护不同的数据就应该加不同的锁。

    所以当有多个互斥锁存在的时候,可能会导致死锁,死锁原理如下:

    复制代码
     1 import threading
     2 import time
     3 def foo():
     4     lockA.acquire()
     5     print('func foo ClockA lock')
     6     lockB.acquire()
     7     print('func foo ClockB lock')
     8     lockB.release()
     9     lockA.release()
    10 
    11 def bar():
    12 
    13     lockB.acquire()
    14     print('func bar ClockB lock')
    15     time.sleep(2)  # 模拟io或者其他操作,第一个线程执行到这,在这个时候,lockA会被第二个进程占用
    16                    # 所以第一个进程无法进行后续操作,只能等待lockA锁的释放
    17     lockA.acquire()
    18     print('func bar ClockA lock')
    19     lockB.release()
    20     lockA.release()
    21 
    22 def run():
    23     foo()
    24     bar()
    25 
    26 lockA=threading.Lock()
    27 lockB=threading.Lock()
    28 for i in range(10):
    29     t=threading.Thread(target=run,args=())
    30     t.start()
    31 
    32 输出结果:只有四行,因为产生了死锁阻断了
    33 func foo ClockA lock
    34 func foo ClockB lock
    35 func bar ClockB lock
    36 func foo ClockA lock
    复制代码

    递归锁(重要)

    解决死锁

    复制代码
     1 import threading
     2 import time
     3 def foo():
     4     rlock.acquire()
     5     print('func foo ClockA lock')
     6     rlock.acquire()
     7     print('func foo ClockB lock')
     8     rlock.release()
     9     rlock.release()
    10 
    11 def bar():
    12     rlock.acquire()
    13     print('func bar ClockB lock')
    14     time.sleep(2)
    15     rlock.acquire()
    16     print('func bar ClockA lock')
    17     rlock.release()
    18     rlock.release()
    19 
    20 
    21 def run():
    22     foo()
    23     bar()
    24 
    25 rlock=threading.RLock() #RLock本身有一个计数器,如果碰到acquire,那么计数器+1
    26                         #如果计数器大于0,那么其他线程无法查收,如果碰到release,计数器-1
    27 
    28 for i in range(10):
    29     t=threading.Thread(target=run,args=())
    30     t.start()
    复制代码

    Semaphore(信号量)

    实际上也是一种锁,该锁用于限制线程的并发量

    以下代码在sleep两秒后会打印出100个ok

    复制代码
    1 import threading
    2 import time
    3 def foo():
    4     time.sleep(2)
    5     print('ok')
    6 
    7 for i in range(100):
    8     t=threading.Thread(target=foo,args=())
    9     t.start()
    复制代码

    每2秒打印5次ok

    复制代码
     1 import threading
     2 import time
     3 sem=threading.Semaphore(5)
     4 def foo():
     5     sem.acquire()
     6     time.sleep(2)
     7     print('ok')
     8     sem.release()
     9 
    10 for i in range(100):
    11     t=threading.Thread(target=foo,args=())
    12     t.start()
    复制代码
  • 相关阅读:
    Oracle数据库系统结构二(实例结构)
    Oracle数据库系统结构一(存储结构)
    SQL Plus的使用详解(登录和常用命令)
    Oracle的基本了解和配置
    Oracle11g的安装及删除
    C++编译预处理
    C++程序的多文件组织
    C++变量的存储类型
    C++动态存储方式与静态存储方式
    C++函数五(局部变量与全局变量和域运算符)
  • 原文地址:https://www.cnblogs.com/chenqizhou/p/7359625.html
Copyright © 2011-2022 走看看