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. Winfrom 简单的安卓手机屏幕获取和安卓简单操作

    为啥我要做这个东西了,是因为经常要用投影演示app ,现在有很多这样的软件可以把手机界面投到电脑上 ,但都要安装,比如说360的手机助手,我又讨厌安装,于是就自己捣鼓了下 做了这个东西, 实现了以下简 ...

  2. Spring - Bean的概念及其基础配置

    概述 bean说白了就是一个普通的java类的实例,我们在bean中写一些我们的业务逻辑,这些实例由Sping IoC容器管理着.在web工程中的spring配置文件中,我们用<bean/> ...

  3. JVM菜鸟进阶高手之路十(基础知识开场白)

    转载请注明原创出处,谢谢! 最近没有什么实战,准备把JVM知识梳理一遍,先以开发人员的交流来谈谈jvm这块的知识以及重要性,依稀记得2.3年前用solr的时候老是经常oom,提到oom大家应该都不陌生 ...

  4. 基于 Electron 的爬虫框架 Nightmare

    作者:William 本文为原创文章,转载请注明作者及出处 Electron 可以让你使用纯 JavaScript 调用 Chrome 丰富的原生的接口来创造桌面应用.你可以把它看作一个专注于桌面应用 ...

  5. JS之脚本延迟

    自从开了博客,我就一下班回来匆匆吃完饭门一关等一开电脑一打开匆匆的研究东西,以至于朋友们都怀疑我是不是都得了自闭症 其实因为我有恐惧心理怕自己的技术哪天跟不上社会了,说到技术我觉得技术不求越新越好,但 ...

  6. yum软件管理器,及yum源配置

    说到yum源就必须说到linux系统中特有的依赖关系问题,yum就是为了解决依赖关系而存在的.yum源就相当是一个目录项,当我们使用yum机制安装软件时,若需要安装依赖软件,则yum机制就会根据在yu ...

  7. CentOS7 +vsftpd (一)之 匿名

    CentOS7 +vsftpd (一)之 匿名 ftp的搭建是一个基础性的工作,CentOS7 +vsftpd 是一个比较容易实现的平台,但在搭建中问题会不少,本系列将通过四篇随笔与大家分享. 一.C ...

  8. Reshape the Matrix

    In MATLAB, there is a very useful function called 'reshape', which can reshape a matrix into a new o ...

  9. 插入排序的性能测试对比(C与C++实现)

    一.概述: [标题]学生成绩管理的设计与实现 [开发语言]C.C++ [主要技术]结构体.STL [基本功能]实现对学生成绩类的基本操作:增加.删除.查询.排序 [测试数据]功能测试:按提示输入5组正 ...

  10. input输入中文时,拼音在输入框内会触发input事件的问题。

    问题描述: 监听文本输入框的input事件,在拼写汉字(输入法)但汉字并未实际填充到文本框中(选词)时会触发input事件,如图: 需要完成的需求就是在输入阶段不触发input中的事件,选词之后文字落 ...