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. def run():
  21. foo()
  22. bar()
  23.  
  24. rlock=threading.RLock() #RLock本身有一个计数器,如果碰到acquire,那么计数器+1
  25. #如果计数器大于0,那么其他线程无法查收,如果碰到release,计数器-1
  26.  
  27. for i in range(10):
  28. t=threading.Thread(target=run,args=())
  29. 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()

Python开发基础-Day30多线程锁机制的更多相关文章

  1. python基础之多线程锁机制

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

  2. Python高阶之多线程锁机制

    '''1.多进程的优势:为了同步完成多项任务,通过提高资源使用效率来提高系统的效率.2.查看线程数:threading.enumerate()函数便可以看到当前线程的数量.3.查看当前线程的名字:th ...

  3. Python开发基础-Day29多线程

    概念 进程:进程就是一个程序在一个数据集上的一次动态执行过程 程序:代码 数据集:程序执行过程中需要的资源 进程控制块:完成状态保存的单元 线程:线程是寄托在进程之上,为了提高系统的并发性 线程是进程 ...

  4. python 多线程锁机制

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

  5. [java多线程] - 锁机制&同步代码块&信号量

    在美眉图片下载demo中,我们可以看到多个线程在公用一些变量,这个时候难免会发生冲突.冲突并不可怕,可怕的是当多线程的情况下,你没法控制冲突.按照我的理解在java中实现同步的方式分为三种,分别是:同 ...

  6. Python开发基础-Day32 进程间通信、进程池、协程

    进程间通信 进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的. 进程队列queue 不同于线程queue,进程 ...

  7. 还在用Alpine作为你Docker的Python开发基础镜像?其实Ubuntu更好一点

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_173 一般情况下,当你想为你的Python开发环境选择一个基础镜像时,大多数人都会选择Alpine,为什么?因为它太小了,仅仅只有 ...

  8. python开发进程:互斥锁(同步锁)&进程其他属性&进程间通信(queue)&生产者消费者模型

    一,互斥锁,同步锁 进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的, 竞争带来的结果就是错乱,如何控制,就是加锁处理 part1:多个进程共享同一打印终 ...

  9. Python开发基础-Day31 Event对象、队列和多进程基础

    Event对象 用于线程间通信,即程序中的其一个线程需要通过判断某个线程的状态来确定自己下一步的操作,就用到了event对象 event对象默认为假(Flase),即遇到event对象在等待就阻塞线程 ...

随机推荐

  1. Oracle Number类型超长小数位为0问题

    碰到了一个非常奇怪的问题,从Excel拷贝出来的数据,位数很长,通过Pl Sql 导出到Oracle后为0了,而且设置查询条件为0时,无法查询出来,条件大于0居然能查询出来,通过to_number也是 ...

  2. 使用generatorConfig工具自动生成mybatis的实体类以及dao接口和映射文件

    1:数据准备 创建一个数据库表 CREATE TABLE `logininfo` ( `id` ) NOT NULL AUTO_INCREMENT, `username` ) DEFAULT NULL ...

  3. MQTT协议及推送服务(二)

    MQTT简介 MQTT全称叫做Message Queuing Telemetry Transport,意为消息队列遥测传输,是IBM开发的一个即时通讯协议.由于其维护一个长连接以轻量级低消耗著称,所以 ...

  4. 2016CCPC东北地区大学生程序设计竞赛 (2018年8月22日组队训练赛)

    题目链接:http://acm.hdu.edu.cn/search.php?field=problem&key=2016CCPC%B6%AB%B1%B1%B5%D8%C7%F8%B4%F3%D ...

  5. 蓝色的cms网站后台管理模板——后台

    链接:http://pan.baidu.com/s/1c138cwC 密码:9vy9

  6. wordpress 模板制作之一

    WP模板工作原理图:

  7. 二进制、十进制、十六进制(python)

    int(“x”,base=2/8/16)是把x都转换成十进制 二进制: 1111=1*2的3次方+1*2的2次方+1*2的1次方+1*2的0次方  =8+4+2+1=15 1000=1*2的3次方+0 ...

  8. perl6 Socket: 发送HTTP请求

    sub MAIN(Str $host,Str $path, Int $port) { my $send = "GET $path HTTP/1.1\r\nHost: $host\r\n\r\ ...

  9. 某团队线下赛AWD writeup&Beescms_V4.0代码审计

    还是跟上篇一样.拿别人比赛的来玩一下.  0x01 预留后门 连接方式: 0x02 后台登录口SQL注入 admin/login.php 在func.php当中找到定义的check_login函数 很 ...

  10. LeetCode818. Race Car

    https://leetcode.com/problems/race-car/description/ Your car starts at position 0 and speed +1 on an ...