IO阻塞分析:

下面该需求很简单将一个数值100做自减处到0.主函数中有0.1秒的IO阻塞

import threading
import time
def sub():
global num # 掌握为什么加global num
temp=num
time.sleep(0.1)
num=temp-1 time.sleep(2)
num=100
l=[]
for i in range(100):
t=threading.Thread(target=sub,args=())
t.start()
l.append(t)
for t in l: #100个线程的IO阻塞
t.join()
print(num) "D:\Program Files\python.exe" E:/py_code/多线程/互斥锁.py
99 Process finished with exit code 0

分析:

通过代码我们可以看到因为主函数中增加了一段IO阻塞的代码,所以我们考虑用到了线程。

1.因为Python GIL的特性原因,对于多线程并不能真正的实现并行而只能并发

2.当第一个线程抢到了GIL后(temp=100),其他的线程暂时不可以执行,但当第一个线程执行到time.sleep(0.1)时(IO阻塞),此时cup是闲置的,第二个线程可以抢GIL。

3.因为第一个线程取到temp=100还没有执行到num=temp-1所以此时第二个线程取到的值还是100,接着也执行到time.sleep(0.1),同理第三个线程抢到GIL。依次类推可以以知道这一百个线程在执行到time.sleep(0.1)之前得到的值都是100,当0.1秒过后全部都依次执行了num=temp-1,所以得出的结果是99.

所以很显然上面的代码没办法得到我们想要的结果,造成的原因就是IO阻塞和GIL相互作用的原因,所以下面会有另外的途径去解决

互斥锁


import threading
import time
def sub():
global num # 掌握为什么加global num
lock.acquire() #加锁直到释放锁下个线程才可以抢锁
temp=num
time.sleep(0.1)
num=temp-1
lock.release() #释放锁下个线程可以抢锁了
time.sleep(2)
num=100
l=[]
lock=threading.Lock() #生成锁对象
for i in range(100): #生成100个线程
t=threading.Thread(target=sub,args=())
t.start()
l.append(t)
for t in l: #100个线程的IO阻塞
t.join()
print(num) "D:\Program Files\python.exe" E:/py_code/多线程/互斥锁.py
0 Process finished with exit code 0

分析:

1.全局定义一把锁,因为进程处理的这个数据num是个公共数据,100个线程进行处理

2.主程序中处理数据的代码只有下面的这三行:

temp=num
time.sleep(0.1)
num=temp-1

所以将这三行代码锁起来,使其在执行完之前其他的线程无法进行干预和取值执行。

3.所以将代码前加锁,数据做完运算后释放锁。当锁释放后其他线程就可以去抢占这把锁。

4.线程的运行流程是:

第一个线程抢到GIL,并拿到值temp=100,开始执行代码,当执行到lock.acquire() 时,下面的代码加锁运行,一直执行到time.sleep(0.1),遇到IO阻塞,第二个线程开始抢GIL,抢到后执行代码执行到lock.acquire(),此时该锁已经被第一个线程抢占还未释放,所以第二个线程只能等第一个线程释放。

5.当0.1s过后第一个线程继续执行代码num=temp-1,此时temp=99,接着执行到lock.release(),锁释放第二个线程抢到开始执行temp=num,而此时的temp=99,接着执行到time.sleep(0.1),第三个线程抢占GIL,但因为第二个线程未释放定义锁,所以无法继续执行,此时第二个线程执行num=temp-1,此时的num=98.依此类推接下来的线程均依据进行运行。所以最后的执行结果等于0

递归锁

死锁:

所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程

import threading
import time
class MyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
self.foo()
self.bar()
def foo(self):
LockA.acquire()
print("I am %s GET LOCKA---->%s"%(self.name,time.ctime()))
LockB.acquire()
print("I am %s GET LOCKB---->%s"%(self.name,time.ctime()))
LockB.release()
LockA.release()
def bar(self):
#time.sleep(2)
LockB.acquire()
print("I am %s GET LOCKB---->%s"%(self.name,time.ctime()))
LockA.acquire()
print("I am %s GET LOCKA---->%s"%(self.name,time.ctime()))
LockA.release()
LockB.release()
LockA=threading.Lock()
LockB=threading.Lock()
for i in range(10):
t=MyThread()
t.start() "D:\Program Files\python.exe" E:/py_code/多线程/互斥锁.py
I am Thread-1 GET LOCKA---->Tue Jul 18 18:46:27 2017
I am Thread-1 GET LOCKB---->Tue Jul 18 18:46:27 2017
I am Thread-1 GET LOCKB---->Tue Jul 18 18:46:27 2017
I am Thread-2 GET LOCKA---->Tue Jul 18 18:46:27 2017

分析:

1.上述的代码定义了两把锁LockA和LockB,并定义一个类,类中继承线程,则MyThread中就可以调用线程的所有方法。

2.在foo函数中在A锁中嵌套了B锁这这里第一个线程进入执行的逻辑是,执行第一个函数foo,执行第一个打印然后启动B锁执行第二个打印释放B锁释放A锁进入到bar函数,于此同时第二个线程进入到要执行foo,当第一个线程释放A锁的那一刻,第二个线程马上取到A锁,但是此时第一个线程已经进入到bar函数也同一时刻取到B锁执行第一个bar函数的第一个打印,当其执行到启动A锁时就会卡住,因为此时的A锁已经被第二个线程拿到,同理第二个线程此时在foo函数中想启动B锁,但是B锁已经被第一个线程拿到,所以这个时候整个进程就会卡住

3.这是在互斥锁中常见的问题,所以我们需要引进第二把锁递归锁:

递归锁

在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁

import threading
import time
class MyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
self.foo()
self.bar()
def foo(self):
RLock.acquire()
print("I am %s GET LOCKA---->%s"%(self.name,time.ctime()))
RLock.acquire()
print("I am %s GET LOCKB---->%s"%(self.name,time.ctime()))
RLock.release()
RLock.release()
def bar(self): RLock.acquire()
print("I am %s GET LOCKB---->%s"%(self.name,time.ctime()))
RLock.acquire()
print("I am %s GET LOCKA---->%s"%(self.name,time.ctime()))
RLock.release()
RLock.release() RLock=threading.RLock()
for i in range(10):
t=MyThread()
t.start() "D:\Program Files\python.exe" E:/py_code/多线程/互斥锁.py
I am Thread-1 GET LOCKA---->Tue Jul 18 19:19:17 2017
I am Thread-1 GET LOCKB---->Tue Jul 18 19:19:17 2017
I am Thread-1 GET LOCKB---->Tue Jul 18 19:19:17 2017
I am Thread-1 GET LOCKA---->Tue Jul 18 19:19:17 2017
I am Thread-2 GET LOCKA---->Tue Jul 18 19:19:17 2017
I am Thread-2 GET LOCKB---->Tue Jul 18 19:19:17 2017
I am Thread-2 GET LOCKB---->Tue Jul 18 19:19:17 2017
I am Thread-2 GET LOCKA---->Tue Jul 18 19:19:17 2017
I am Thread-4 GET LOCKA---->Tue Jul 18 19:19:17 2017
I am Thread-4 GET LOCKB---->Tue Jul 18 19:19:17 2017
I am Thread-4 GET LOCKB---->Tue Jul 18 19:19:17 2017
I am Thread-4 GET LOCKA---->Tue Jul 18 19:19:17 2017
I am Thread-5 GET LOCKA---->Tue Jul 18 19:19:17 2017
I am Thread-5 GET LOCKB---->Tue Jul 18 19:19:17 2017
I am Thread-5 GET LOCKB---->Tue Jul 18 19:19:17 2017
I am Thread-5 GET LOCKA---->Tue Jul 18 19:19:17 2017
I am Thread-6 GET LOCKA---->Tue Jul 18 19:19:17 2017
I am Thread-6 GET LOCKB---->Tue Jul 18 19:19:17 2017
I am Thread-6 GET LOCKB---->Tue Jul 18 19:19:17 2017
I am Thread-6 GET LOCKA---->Tue Jul 18 19:19:17 2017
I am Thread-3 GET LOCKA---->Tue Jul 18 19:19:17 2017
I am Thread-3 GET LOCKB---->Tue Jul 18 19:19:17 2017
I am Thread-3 GET LOCKB---->Tue Jul 18 19:19:17 2017
I am Thread-3 GET LOCKA---->Tue Jul 18 19:19:17 2017
I am Thread-8 GET LOCKA---->Tue Jul 18 19:19:17 2017
I am Thread-8 GET LOCKB---->Tue Jul 18 19:19:17 2017
I am Thread-8 GET LOCKB---->Tue Jul 18 19:19:17 2017
I am Thread-8 GET LOCKA---->Tue Jul 18 19:19:17 2017
I am Thread-9 GET LOCKA---->Tue Jul 18 19:19:17 2017
I am Thread-9 GET LOCKB---->Tue Jul 18 19:19:17 2017
I am Thread-9 GET LOCKB---->Tue Jul 18 19:19:17 2017
I am Thread-9 GET LOCKA---->Tue Jul 18 19:19:17 2017
I am Thread-7 GET LOCKA---->Tue Jul 18 19:19:17 2017
I am Thread-7 GET LOCKB---->Tue Jul 18 19:19:17 2017
I am Thread-7 GET LOCKB---->Tue Jul 18 19:19:17 2017
I am Thread-7 GET LOCKA---->Tue Jul 18 19:19:17 2017
I am Thread-10 GET LOCKA---->Tue Jul 18 19:19:17 2017
I am Thread-10 GET LOCKB---->Tue Jul 18 19:19:17 2017
I am Thread-10 GET LOCKB---->Tue Jul 18 19:19:17 2017
I am Thread-10 GET LOCKA---->Tue Jul 18 19:19:17 2017 Process finished with exit code 0

分析:

1.上述的代码在全局定义了一把递归锁

2.当第一个线程进入执行时先进入到foo函数,启动第一把锁并记录一次接着执行第一个打印,然后启动第二把锁执行第二次打印并释放两把锁。此时包括第一个线程在内这10个线程都能进行抢锁,这里RLock采取的就近原则,所以还是第一个线程抢到并进入到bar函数,此时其他线程是抢不到锁只能继续等待线程一释放,线程一顺序执行完bar函数并释放RLock锁,此刻线程一已经完整的执行完整个工作,并且线程2抢到锁执行过程和线程一相同。

3.依次类推这10个线程会先后执行完各自的工作。如上述代码执行的结果所示

信号量

Semaphore管理一个内置的计数器,

每当调用acquire()时内置计数器-1;

调用release() 时内置计数器+1;

计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()

import threading
import time
semaphore=threading.Semaphore(5) def foo():
semaphore.acquire()
time.sleep(2)
print("ok")
semaphore.release() for i in range(100):
t=threading.Thread(target=foo,args=())
t.start()

分析:

1.全局定义一个信号量,并限制最大接入的线程数为5个

2.执行逻辑,当第一个线程进入后semaphore内置计数减1,则这里可以接收4个线程,此时第一个线程打印第一个OK,接着第二个线程进入打印第二个OK,一直到第五个线程进入后,这时第六个线程在第五个线程没有执行完semaphore.release()时是不可以进入的,所以按照这个逻辑,该端代码的执行结果是每5个OK打印一次一共打印100个OK.

Python线程的常见的lock的更多相关文章

  1. python笔记10-多线程之线程同步(锁lock)

    前言 关于吃火锅的场景,小伙伴并不陌生,吃火锅的时候a同学往锅里下鱼丸,b同学同时去吃掉鱼丸,有可能会导致吃到生的鱼丸. 为了避免这种情况,在下鱼丸的过程中,先锁定操作,让吃火锅的小伙伴停一会,等鱼丸 ...

  2. Python 线程,with的作用(自动获取和释放锁Lock)

    Python 线程,with的作用(自动获取和释放锁Lock) import threading import time num= #全局变量多个线程可以读写,传递数据 mutex=threading ...

  3. python线程互斥锁Lock(29)

    在前一篇文章 python线程创建和传参 中我们介绍了关于python线程的一些简单函数使用和线程的参数传递,使用多线程可以同时执行多个任务,提高开发效率,但是在实际开发中往往我们会碰到线程同步问题, ...

  4. 操作系统/应用程序、操作中的“并发”、线程和进程,python中线程和进程(GIL锁),python线程编写+锁

    并发编程前言: 1.网络应用 1)爬虫 直接应用并发编程: 2)网络框架 django flask tornado 源码-并发编程 3)socketserver 源码-并发编程 2.运维领域 1)自动 ...

  5. python 线程、进程与协程

    一.什么是线程?什么是进程? 第一,进程是一个实体.每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region).数据区域(data region)和堆栈(stack regio ...

  6. python线程事件Event(30)

    在python项目开发中,线程thread使用是比较常见的,在前面的文章中我们介绍了 python线程的创建 以及 线程互斥锁 ,今天还要额外介绍一个与线程相关的内容 – 事件Event. 一.pyt ...

  7. python——线程与多线程进阶

    之前我们已经学会如何在代码块中创建新的线程去执行我们要同步执行的多个任务,但是线程的世界远不止如此.接下来,我们要介绍的是整个threading模块.threading基于Java的线程模型设计.锁( ...

  8. Python 线程、进程和协程

    python提供了两个模块来实现多线程thread 和threading ,thread 有一些缺点,在threading 得到了弥补,为了不浪费时间,所以我们直接学习threading 就可以了. ...

  9. [python] 线程锁

    参考:http://blog.csdn.net/kobeyan/article/details/44039831 1. 锁的概念 在python中,存在GIL,也就是全局解释器锁,能够保证同一时刻只有 ...

随机推荐

  1. 201521123052《Java程序设计》第5周学习总结

    1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 1.2 可选:使用常规方法总结其他上课内容. 学习了更多markdown的知识 参考资料: 百度脑图 XMind 2. 书面作 ...

  2. 201521123005 《Java程序设计》 第十四周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多数据库相关内容. 2. 书面作业 Q1. MySQL数据库基本操作 建立数据库,将自己的姓名.学号作为一条记录插入.(截图,需出现 ...

  3. 201521123052 《Java程序设计》 第12周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 2. 书面作业 将Student对象(属性:int id, String name,int age,doubl ...

  4. Eclipse集成spket插件_Ext-5.1.0

    1, spket-1.6.23.jar下载 官网下载:http://www.spket.com/download.html ,  百度网盘地址: http://pan.baidu.com/s/1qXH ...

  5. Python: 列表注意细节与元组的基本用法

    列表注意细节: 1.list.clear():将列表中成员清空(与del list区别开) 2.list.copy():复制一份相同的列表(浅COPY,只复制列表第一层) 3.如果两个列表相等,如li ...

  6. 【漏洞公告】CVE-2017-12615/CVE-2017-12616:Tomcat信息泄漏和远程代码执行漏洞

    2017年9月19日,Apache Tomcat官方确认并修复了两个高危漏洞,漏洞CVE编号:CVE-2017-12615和CVE-2017-12616,该漏洞受影响版本为7.0-7.80之间,在一定 ...

  7. NET_NET深入体验与实战 第一章 .NET你知道 1.1什么是 .NET

    1.1什么是 .NET 1.微软定义:Microsft.NET 是微软以  Web Service为核心的,支持 信息,人,系统的一组软件产品,技术或者服务. 2.战略和梦想:(1) Microsft ...

  8. BigDecimal的加减乘除及比较大小

    import java.math.BigDecimal; import static java.lang.System.out; public class BaseClass { public sta ...

  9. String.getBytes(),源码之下,了无秘密

    @Deprecated public void getBytes(int srcBegin, int srcEnd, byte dst[], int dstBegin) { if (srcBegin ...

  10. popOver 弹出框简单使用

    1.仿QQ弹出框 1.1用到的知识点 1.1.1如何调整弹出框的大小(这里弹出的也是控制器) 这里已经有讲解过http://blog.csdn.net/iostiannan/article/detai ...