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. 201521123006 《Java程序设计》 第2周学习总结

    1. 本周学习总结 本周进一步学习了java,了解了java编程中一些特定的用法,比如:在编译程序时可以使用import来减少输入包名称.本周还学会了使用枚举类(enum Choice{fab,sor ...

  2. 201521123095 《Java程序设计》第2周学习总结

    1. 本周学习总结 学会了使用码云管理代码,使用eclipse关联jdk源代码: 还学习了Java语言中各种数据类型以及运算符. 2. 书面作业 1.使用Eclipse关联jdk源代码,并查看Stri ...

  3. 201521123098 JAVA课程设计

    1.团队课程设计博客链接 http://www.cnblogs.com/agts/p/7067948.html 2.个人负责模块或任务说明 个人任务:实现初始界面中的登录.注册模块,以及数据库的连接和 ...

  4. 火狐html5拖拽 弹出新页面解决办法

    今天做项目时,需要实现一个拖拽排序的功能,遂想到了html5的拖拽,便开始查资料,写代码.功夫不复有心人,通过网上资料作参考,排序功能成功实现.谷歌浏览器测试,拖拽平滑,无问题.火狐浏览器测试时,却无 ...

  5. jvm系列:Java GC 分析

    Java GC就是JVM记录仪,书画了JVM各个分区的表演. 什么是 Java GC Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之 ...

  6. win10- *.msi 软件的安装,比如 SVN安装报2503,2502

    1, 以管理员身份打开cmd ,   C:\Windows\System32\cmd.exe 2,输入: msiexec /package "安装文件的全路径" 3,按下回车. 例 ...

  7. SimpleRpc-序列化与反序列化的设计与实现

    为什么需要序列化和反序列化? 假设你是客户端,现在要调用远程的加法计算服务,你与服务端商定好了发送数据的格式:发送8个字节的请求,前4字节是第一个数,后4字节是第二个数,服务端读取数据的时候也按照商定 ...

  8. java 使用redis 数据库

    [TOC] java 使用redis 数据库 连接redis package com.wsc.redis.Test1; import java.util.List; import java.util. ...

  9. String,StringBuffer,StringBuilder的区别及其源码分析

    String,StringBuffer,StringBuilder的区别这个问题几乎是面试必问的题,这里做了一些总结: 1.先来分析一下这三个类之间的关系 乍一看它们都是用于处理字符串的java类,而 ...

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

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