“死锁” 与 python多线程之threading模块下的锁机制
一:死锁
在死锁之前需要先了解的概念是“可抢占资源”与“不可抢占资源”【此处的资源可以是硬件设备也可以是一组信息】,因为死锁是与不可抢占资源有关的。
可抢占资源:可以从拥有他的进程中抢占而不会发生副作用。e.g:存储器就是一类可抢占资源(假设有A, B两个进程都想用打印机对256MB的用户内存进行打印,若A已经获得打印机并且开始打印,但是在没有打印完成其时间片就用完并被换出了,此时B进程开始运行“抢占了”内存并开始请求打印机,但是A进程还拥有打印机所以B进程没有抢占打印机成功,此时由于双方都缺少一个对方拥有的资源,双方都不能执行,有死锁的危险,不过实际上系统会把进程B换出内存,重新将进程A换入内存 [也就是A进程反过来抢占了B进程的内存资源],效果也就是进程A继续执行直到完成并释放打印机,而后B进程就能够执行了)。从中可以看出由于可抢占资源可以通过重新分配资源而避免了死锁的发生,所以可抢占资源是不会发生死锁现象的。
不可抢占资源:在不引起相关计算失败的情况下,无法把它从占有它的进程处抢占过来的资源。e.g:刻录机(假设一个进程已经开始刻盘,突然将刻录机分配给另一个进程,那么将划坏CD盘,所以任何时候刻录机都是不能被抢占的)
死锁:当多个进程需要排他性的访问多个资源,且其所需要的资源在同一时间属于不同进程且还是不可抢占资源的时候,此时相关进程就会一直请求对应资源但得不到从而一直阻塞下去,这就是死锁现象。
*除了硬件机器与I/O设备之外,别的情况也可能引起死锁,比如一个数据库系统中,为了避免竞争可对若干记录加锁,假设进程A对记录R1加锁而进程B岁记录R2加锁,接着他们又各自试着加锁对方的记录,此时也会发生死锁
注:关于GIL(global interpreter lock),可参见这篇文章:http://www.dabeaz.com/python/UnderstandingGIL.pdf
二:Threading模块
2.1.threading.Thread对象【创建线程的主要对象】:
方法:start():启动线程
run():启动线程后自动调用的方法
join([timeout]):等待到被调用的线程终止
is_alive():返回线程活动状态
属性:name:线程名
ident:线程ID号
daemon:后台标志
2.2.创建线程的方法:
1:实例化threading.Thread对象
ths = threading.Thread( target = None, name = None, args = (), kwarg = {} )
传入函数名 线程名 函数的参数
2:继承threading中的Thread对象后再实例化并启动
2.3.以不同的线程类型分:
令:t = threading.Thread(target=dmn...),即是t为一个线程
2.3.1.独立线程:就是最简单的独立单线程
2.3.2.线程等待:t.join([timeout]),放到start()后,表明t线程执行完后(或是指定时间后)才轮到下一个线程【若是因为指定时间的话还会跳回t线程】
#coding:utf-8
import threading, time
class MyThread(threading.Thread): def run(self):
for i in range(40):
print() #若是t线程在执行时就是打印换行
time.sleep(0.1) if __name__ == "__main__":
t = MyThread()
t.start()
**t.join()**
for i in range(10): #此部分是主线程执行的,也就是说此程序有主线程与t线程两个线程
print("main:", i)
time.sleep(0.1) #最后会在打印完了换行后才打印0到9的“main:...”, 说明t线程执行完了后主线程才执行的
线程等待示例
2.3.3.后台线程(在主线程退出后立即自杀):设置daemon属性为True【 t.daemon = True】
2.3.4.线程同步:指的是多个线程同时需要用到的资源,此时为了保证一致性有多种不同的加锁处理机制【获取了锁的线程可以对此资源进行操作,而没有的线程处于阻塞状态】,如下:
1)先到先用---指令锁:threading.Lock对象
定义锁变量:lock = threading.Lock()
锁定资源:lock.acquire()
释放资源:lock.release()
import threading, time, random
share = 4
class MyThread(threading.Thread):
def __init__(self,i):
super().__init__()
self.i = i def run(self):
global share
for d in range(40):
**lock.acquire()**
print(share)
share += self.i
time.sleep(random.random())
print("t+", self.i, "=", share)
**lock.release()** **lock = threading.Lock()** if __name__ == "__main__":
t = MyThread(2)
tt = MyThread(6)
t.start()
# t.join()
tt.start() #会看到一会儿加2,一会儿加6
#若将倒数第二行注释去除,则一直加2,完了后才加6
指令锁示例
2)可重入锁:被某一线程锁定了后还可以被其他线程锁定
3)有先后顺序的使用---条件变量:threading.Condition对象(创建了一个带锁的线程池), [当具备了某条件后才调用某线程,如生产者-消费者模式]
定义锁变量:conLock = threading.Condition()
获得锁:conLock.acquire()
通知线程池里其他线程:conLock.notify( [ n=x ] )【唤起x个,默认为1】,clock.notify_all()【唤醒全部】
释放锁并且进入线程池等待:conLock.wait()
#coding:utf-8
import threading, time
share = 0
**share_condition = threading.Condition()** class ProductThread(threading.Thread):
def __init__(self):
super().__init__()
self.name = "生产者" def run(self):
global share
if share_condition.acquire():
while True:
if not share:
share += 1
print( self.name, share )
**share_condition.notify()** #执行线程池里的一个等待线程
**share_condition.wait()** #当前线程释放锁并将当前线程放入线程池等待下次notify(),所以如果没有此句则只会显示一句“生产者:1”
time.sleep(1) class CustumerThread(threading.Thread):
def __init__(self):
super().__init__()
self.name = "消费者" def run(self):
global share
if share_condition.acquire():
while True:
if share:
share -= 1
print( self.name, share )
**share_condition.notify()**
**share_condition.wait()**
time.sleep(1) lock = threading.Lock() if __name__ == "__main__":
product = ProductThread()
customer = CustumerThread()
product.start()
customer.start() #最后会相继打印:
# 生产者 1
# 消费者 0
条件变量锁机制示例
4)部分线程同时使用---信号量:threading.Semaphore对象
定义锁:sema = threading.Semaphore(n) #允许同时有n个线程获得锁
获得锁:sema.acquire()
释放锁:sema.release()
常用于限制资源的访问,比如数据库的连接池
#coding:utf-8
import threading, time
class MyThread(threading.Thread):
def __init__(self,name):
super().__init__()
self.name = name def run(self):
if sema.acquire():
print(self.name,"得到了锁")
time.sleep(1)
sema.release()
print(self.name,'释放了锁') sema = threading.Semaphore(2) if __name__ == "__main__":
threads = [ MyThread(str(i)+"sema") for i in range(5) ]
for thread in threads:
thread.start() #输出如下,可以看出同时能够有两个线程获得了锁
# 0sema 得到了锁
# 1sema 得到了锁
# 0sema 释放了锁
# 1sema 释放了锁
# 2sema 得到了锁
# 3sema 得到了锁
# 2sema 释放了锁
# 4sema 得到了锁
# 3sema 释放了锁
# 4sema 释放了锁
信号量锁示例
5)线程间通信---threading.Event对象(创建了一个带有标志的线程池,当一个线程设置内部标志为True后线程池中的等待线程就会被唤醒并执行)【即是当前执行线程可以决定啥时候(一般是所共同使用的资源已经使用完毕了后)“叫醒”别的线程,需要注意的是,若此时叫醒别人的线程并没有wait进入线程池,那么如果其还有代码的话将继续执行下去】
定义锁变量: event = threading.Event()
进入线程池等待:event.wait([timeout])
设置内部标志为True:event.set() <---> event.clear()
#coding:utf-8
import threading, time class WaitThread(threading.Thread): def run(self):
self.name = "等待线程"
print(self.name,"正在等待") #注意此程序的执行顺序,先调用的此线程执行到这里
**event.wait()** #在此此线程wait进入线程池等待,切换到mainThread直至设置标志
print(self.name,"启动了") #从而此线程在此被唤醒执行
**event.clear()** #而后设置标志为false class MainThread(threading.Thread): def run(self):
time.sleep(3)
print("主线程更改了标志")
**event.set()**
print("这里的会在WaitThread打印启动了前打印吗?") **event = threading.Event()** if __name__ == "__main__":
wt = WaitThread()
mt = MainThread()
wt.start()
mt.start() # 输出:
# 等待线程 正在等待
# 主线程更改了标志
# 这里的会在WaitThread打印启动了前打印吗?
# 等待线程 启动了
线程间通信锁示例
*补充.关于定时执行:threading.Timer()
参考:
《现代操作系统》---Andrew S. Tanebaum
“死锁” 与 python多线程之threading模块下的锁机制的更多相关文章
- python多线程之threading模块
threading模块中的对象 其中除了Thread对象以外,还有许多跟同步相关的对象 threading模块支持守护线程的机制 Thread对象 直接调用法 import threading imp ...
- python 多线程编程之threading模块(Thread类)创建线程的三种方法
摘录 python核心编程 上节介绍的thread模块,是不支持守护线程的.当主线程退出的时候,所有的子线程都将终止,不管他们是否仍在工作. 本节开始,我们开始介绍python的另外多线程模块thre ...
- python多线程之Threading
什么是线程? 线程是操作系统内核调度的基本单位,一个进程中包含一个或多个线程,同一个进程内的多个线程资源共享,线程相比进程是“轻”量级的任务,内核进行调度时效率更高. 多线程有什么优势? 多线程可以实 ...
- python多线程之threading、ThreadPoolExecutor.map
背景: 某个应用场景需要从数据库中取出几十万的数据时,需要对每个数据进行相应的操作.逐个数据处理过慢,于是考虑对数据进行分段线程处理: 方法一:使用threading模块 代码: # -*- codi ...
- python 线程之 threading(四)
python 线程之 threading(三) http://www.cnblogs.com/someoneHan/p/6213100.html中对Event做了简单的介绍. 但是如果线程打算一遍一遍 ...
- python 线程之 threading(三)
python 线程之 threading(一)http://www.cnblogs.com/someoneHan/p/6204640.html python 线程之 threading(二)http: ...
- python并发编程之threading线程(一)
进程是系统进行资源分配最小单元,线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.进程在执行过程中拥有独立的内存单元,而多个线程共享内存等资源. 系列文章 py ...
- {Python之线程} 一 背景知识 二 线程与进程的关系 三 线程的特点 四 线程的实际应用场景 五 内存中的线程 六 用户级线程和内核级线程(了解) 七 python与线程 八 Threading模块 九 锁 十 信号量 十一 事件Event 十二 条件Condition(了解) 十三 定时器
Python之线程 线程 本节目录 一 背景知识 二 线程与进程的关系 三 线程的特点 四 线程的实际应用场景 五 内存中的线程 六 用户级线程和内核级线程(了解) 七 python与线程 八 Thr ...
- <python的线程与threading模块>
<python的线程与threading模块> 一 线程的两种调用方式 threading 模块建立在thread 模块之上.thread模块以低级.原始的方式来处理和控制线程,而thre ...
随机推荐
- windows server 2012安装.net framework3.5失败解决方法
1.点击开始运行,输入 gpedit.msc,打开“组策略”2.“计算机配置”---“管理模板”---“windows 组件”---“windows 更新”,然后双击“指定 Intranet Micr ...
- 2、MyBatis.NET学习笔记之CodeSmith使用
说明:本系列随笔会与CSDN同步发布,当然这里先发,因为这里可以用WLW.但刚才由于误操作,没有重新发上来.只好先在CSDN先发了.重往这里发时图片无法处理,索性直接粘过来吧! 使用框架后一些相关的配 ...
- BZOJ 1033 杀蚂蚁
Description 最近,佳佳迷上了一款好玩的小游戏:antbuster.游戏规则非常简单:在一张地图上,左上角是蚂蚁窝,右下角是蛋糕,蚂蚁会源源不断地从窝里爬出来,试图把蛋糕搬回蚂蚁窝.而你的任 ...
- 跨进程发送消息数据(发送WM_COPYDATA消息,够简单的)
1 //1.发送窗体 2 procedure TForm2.Button1Click(Sender: TObject); 3 var 4 h: HWND; 5 Size: Integer; 6 Cop ...
- VS2012中使用编译的Qt-5.1.1静态库开发程序
1. 安装qt-vs-addin-1.2.2-opensource.exe,安装之后,vs2012菜单栏就会出现QT5菜单. 2. 在qt-vs-addin添加Qt5静态库: QT5->Qt O ...
- Linux [Ubuntu 12.0.1] 常用命令
1.文件名颜色的含义1)默认色代表普通文件.例:install.log2)绿色代表可执行文件.例:rc.news3)红色代表tar包 文件. 例:vim-7.1.tar.bz24)蓝色代表目录文件. ...
- Delphi NativeXml用法攻略
NativeXml用法攻略 NativeXml可以在官网上下载,下载后将文件夹放在指定地方,打开DELPHI在其环境变量中引用NativeXml路径,然后在程序中引用NativeXml单元,我们就可以 ...
- 我的学习笔记之node----node.js+socket.io实时聊天(2)
废话不多说,直接贴代码吧.注释很详细了. 服务端代码: /** * Created by LZX on 2015/10/7. */(function () { var d = document, w ...
- UVA 11922 Permutation Transformer(Splay Tree)
题目链接: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=18902 [思路] 伸展树+打标记. 用伸展树维护这个序列,使得能 ...
- Break、continue、return用法(C++)
(1)break 直接调出当前循环体.如果是嵌套循环,他只能调出一层循环体. Exp-1: 程序: #include<iostream> using namespace std; int ...