python同步、互斥锁、死锁
同步
同步的概念
同步就是协同步调,按预定的先后次序进行运行。如:你说完,我再说。"同"字从字面上容易理解为一起动作,其实不是,"同"字应是指协同、协助、互相配合。如进程、线程同步,可理解为进程或线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B执行,再将结果给A;A再继续操作。
解决线程同时修改全局变量的方式
对于上一篇中(多线程共享全局变量)的那个计算错误的问题,可以通过线程同步来进行解决。
思路,如下:
- 系统调用t1,然后获取到g_num的值为0,此时上一把锁,即不允许其他线程操作g_num
- t1对g_num的值进行+1
- t1解锁,此时g_num的值为1,其他的线程就可以使用g_num了,而且是g_num的值不是0而是1
- 同理其他线程在对g_num进行修改时,都要先上锁,处理完后再解锁,在上锁的整个过程中不允许其他线程访问,就保证了数据的正确性
互斥锁
当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制。线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。
互斥锁为资源引入一个状态:锁定/非锁定
某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
threading模块中定义了Lock类,可以方便的处理锁定:
# 创建锁
mutex = threading.Lock()
# 锁定
mutex.acquire()
# 释放
mutex.release()
注意:
- 如果这个锁之前是没有上锁的,那么acquire不会堵塞
- 如果在调用acquire对这个锁上锁之前 它已经被 其他线程上了锁,那么此时acquire会堵塞,直到这个锁被解锁为止
使用互斥锁完成2个线程对同一个全局变量各加9999999 次的操作
import threading
import time
g_num = 0
# 创建一个互斥锁,默认为未上锁zhuangtai
mutex = threading.Lock()
def test1(num):
global g_num
for i in range(num):
mutex.acquire()
g_num += 1
mutex.release()
print("--test1, g_num = %d--" % g_num)
def test2(num):
global g_num
for i in range(num):
mutex.acquire()
g_num += 1
mutex.release()
print("--test2, g_num = %d--" % g_num)
if __name__ == "__main__":
print("--创建线程之前, g_num = %d--" % g_num)
t1 = threading.Thread(target=test1, args=(9999999,))
t1.start()
t2 = threading.Thread(target=test2, args=(9999999,))
t2.start()
while len(threading.enumerate()) != 1:
time.sleep(1)
print("最终结果为:g_num=%d" % g_num)
运行结果:
--创建线程之前, g_num = 0--
--test1, g_num = 19580251--
--test2, g_num = 19999998--
最终结果为:g_num=19999998
可以看到最后的结果,加入互斥锁后,其结果与预期相符。
上锁解锁过程
当一个线程调用锁的acquire()方法获得锁时,锁就进入“locked”状态。
每次只有一个线程可以获得锁。如果此时另一个线程试图获得这个锁,该线程就会变为“blocked”状态,称为“阻塞”,直到拥有锁的线程调用锁的release()方法释放锁之后,锁进入“unlocked”状态。
线程调度程序从处于同步阻塞状态的线程中选择一个来获得锁,并使得该线程进入运行(running)状态。
总结
锁的好处:
- 确保了某段关键代码只能由一个线程从头到尾完整地执行
锁的坏处:
- 阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了
- 由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁
死锁
在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。
尽管死锁很少发生,但一旦发生就会造成应用的停止响应。下面看一个死锁的例子
import threading
import time
class MyThread1(threading.Thread):
def run(self):
# 对mutex1上锁
mutex1.acquire()
# mutex1上锁后,延迟1秒,等待另外一个线程,把mutex2上锁
print(self.name + " up")
time.sleep(1)
# 此时这里会堵塞,因为mutex2已经被另外的线程抢先上锁了
mutex2.acquire()
print(self.name + " down")
mutex2.release()
# 对mutex1解锁
mutex1.release()
class MyThread2(threading.Thread):
def run(self):
# 对mutex2上锁
mutex2.acquire()
# mutex2上锁后,延迟1秒,等待另外一个线程,把mutex1上锁
print(self.name + " up")
time.sleep(1)
# 此时这里会堵塞,因为mutex1已经被另外的线程抢先上锁了
mutex1.acquire()
print(self.name + " down")
mutex1.release()
# 对mutex2解锁
mutex2.release()
mutex1 = threading.Lock()
mutex2 = threading.Lock()
if __name__ == "__main__":
t1 = MyThread1()
t1.start()
t2 = MyThread2()
t2.start()
运行结果:
Thread-1 up
Thread-2 up
此时已经进入到了死锁状态,可以使用ctrl-c退出
避免死锁
- 程序设计时要尽量避免(银行家算法)
- 添加超时时间等
python同步、互斥锁、死锁的更多相关文章
- python线程互斥锁Lock(29)
在前一篇文章 python线程创建和传参 中我们介绍了关于python线程的一些简单函数使用和线程的参数传递,使用多线程可以同时执行多个任务,提高开发效率,但是在实际开发中往往我们会碰到线程同步问题, ...
- Python Threading 线程/互斥锁/死锁/GIL锁
导入线程包 import threading 准备函数线程,传参数 t1 = threading.Thread(target=func,args=(args,)) 类继承线程,创建线程对象 class ...
- Linux 线程编程2.0——线程同步-互斥锁
当我们需要控制对共享资源的存取的时候,可以用一种简单的加锁的方法来控制.我们可以创建一个读/写程序,它们共用一个共享缓冲区,使用互斥锁来控制对缓冲区的存取. 函数 pthread_mutex_init ...
- 并发编程~~~多线程~~~守护线程, 互斥锁, 死锁现象与递归锁, 信号量 (Semaphore), GIL全局解释器锁
一 守护线程 from threading import Thread import time def foo(): print(123) time.sleep(1) print('end123') ...
- go同步互斥锁
import "sync" var ( myMap = make(map[int]int, 10) lock sync.Mutex //声明一个全局的互斥锁 //sync 包 同步 ...
- C++11 多线程同步 互斥锁 条件变量
在多线程程序中,线程同步(多个线程访问一个资源保证顺序)是一个非常重要的问题,Linux下常见的线程同步的方法有下面几种: 互斥锁 条件变量 信号量 这篇博客只介绍互斥量和条件变量的使用. 互斥锁和条 ...
- Python的互斥锁与信号量
并发与锁 a. 多个线程共享数据的时候,如果数据不进行保护,那么可能出现数据不一致现象,使用锁,信号量.条件锁 b. c.互斥锁1. 互斥锁,是使用一把锁把代码保护起来,以牺牲性能换取代码的安全性,那 ...
- Python多线程同步互斥锁
接着上篇多线程继续讲,上篇最后的多线程共享全局变量对变量的处理值出错在本文中给出解决方案. 出现这个情况的原因是在python解释器中GIL全局解释器锁. GIL:全局解释器锁,每个线程在执行的过程都 ...
- Python 锁 同步 互斥锁
import time from threading import Lock,Thread num = 100 def f1(loc): loc.acquire() global num tmp = ...
- Python多线程互斥锁
import threading import time num=0 def Mylock(lock): global num lock.acquire() num=num+1 time.sleep( ...
随机推荐
- React 和 Redux 结合 1
React依赖: "devDependencies": { "babel-core": "^6.26.0", "babel-loa ...
- 背水一战 Windows 10 (107) - 通知(Toast): 提示音, 特定场景
[源码下载] 背水一战 Windows 10 (107) - 通知(Toast): 提示音, 特定场景 作者:webabcd 介绍背水一战 Windows 10 之 通知(Toast) 提示音 特定场 ...
- 背水一战 Windows 10 (93) - 选取器: FileOpenPicker, FolderPicker, FileSavePicker
[源码下载] 背水一战 Windows 10 (93) - 选取器: FileOpenPicker, FolderPicker, FileSavePicker 作者:webabcd 介绍背水一战 Wi ...
- ISG 2018 Web Writeup
作者:agetflag 原文来自:ISG 2018 Web Writeup ISG 2018 Web Writeup CTF萌新,所以写的比较基础,请大佬们勿喷,比赛本身的Web题也不难 calc 首 ...
- Android 9.0/P 开发问题及解决方案汇总
一.使用 org.apache.http.legacy 库在Android 9.0上运行出现崩溃 日志内容: java.lang.NoClassDefFoundError: Failed resolu ...
- Java回调机制总结
调用和回调机制 在一个应用系统中, 无论使用何种语言开发, 必然存在模块之间的调用, 调用的方式分为几种: 1.同步调用 同步调用是最基本并且最简单的一种调用方式, 类A的方法a()调用类B的方法b( ...
- cpuset
本文属于内核文档翻译,翻译时没有遵照原文,添加了一些作者的理解,目的不是为了替代内核文档,可以作为阅读内核文档的引子,作者鼓励读者阅读原有的内核文档.原文参考3.10.514内核文档cpuset.tx ...
- C# Windows异步I/O操作
1.简介 关于Windows的异步I/O操作,只要解决的是同步I/O操作的线程利用率问题,通过异步I/O Api来提升线程的利用率,提升系统的吞吐能力,将各种I/O操作交给线程池然后交由硬件设备执行, ...
- 剑指offer例题分享--6
前言:继续整理例题,快速做完这部分,然后继续用C++去刷数据结构和算法的题. 面试题28: 代码如下: #include<iostream> #include<stdio.h> ...
- 【教程】在UEFI启动方式下,通过GRUB2引导,直接从硬盘ISO文件安装Windows10和Ubuntu双系统
本文为作者原创,允许转载,但必须注明原文地址: https://www.cnblogs.com/byronxie/p/9949789.html 动机 最近在自学MIT6.828 Operating S ...