“死锁” 与 python多线程之threading模块下的锁机制

一:死锁python

  在死锁以前须要先了解的概念是“可抢占资源”与“不可抢占资源”【此处的资源能够是硬件设备也能够是一组信息】,由于死锁是与不可抢占资源有关的。数据库

  可抢占资源:能够从拥有他的进程中抢占而不会发生反作用。e.g:存储器就是一类可抢占资源(假设有A, B两个进程都想用打印机对256MB的用户内存进行打印,若A已经得到打印机而且开始打印,可是在没有打印完成其时间片就用完并被换出了,此时B进程开始运行“抢占了”内存并开始请求打印机,可是A进程还拥有打印机因此B进程没有抢占打印机成功,此时因为双方都缺乏一个对方拥有的资源,双方都不能执行,有死锁的危险,不过实际上系统会把进程B换出内存,从新将进程A换入内存 [也就是A进程反过来抢占了B进程的内存资源],效果也就是进程A继续执行直到完成并释放打印机,然后B进程就可以执行了)。从中能够看出因为可抢占资源能够经过从新分配资源而避免了死锁的发生,因此可抢占资源是不会发生死锁现象的。dom

  不可抢占资源:在不引发相关计算失败的状况下,没法把它从占有它的进程处抢占过来的资源。e.g:刻录机(假设一个进程已经开始刻盘,忽然将刻录机分配给另外一个进程,那么将划坏CD盘,因此任什么时候候刻录机都是不能被抢占的)ide

  死锁:当多个进程须要排他性的访问多个资源,且其所须要的资源在同一时间属于不一样进程且仍是不可抢占资源的时候,此时相关进程就会一直请求对应资源但得不到从而一直阻塞下去,这就是死锁现象。函数

   *除了硬件机器与I/O设备以外,别的状况也可能引发死锁,好比一个数据库系统中,为了不竞争可对若干记录加锁,假设进程A对记录R1加锁而进程B岁记录R2加锁,接着他们又各自试着加锁对方的记录,此时也会发生死锁ui

 

注:关于GIL(global interpreter lock),可参见这篇文章:http://www.dabeaz.com/python/UnderstandingGIL.pdfspa

二:Threading模块操作系统

2.1.threading.Thread对象【建立线程的主要对象】:线程

  方法:start():启动线程code

       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线程】

 1 #coding:utf-8
 2 import threading, time
 3 class MyThread(threading.Thread):
 4 
 5     def run(self):
 6         for i in range(40):
 7             print()    #如果t线程在执行时就是打印换行
 8             time.sleep(0.1)
 9 
10 if __name__ == "__main__":
11     t = MyThread()
12     t.start()
13     **t.join()**
14     for i in range(10):    #此部分是主线程执行的,也就是说此程序有主线程与t线程两个线程
15         print("main:", i)
16         time.sleep(0.1)    
17 
18 #最后会在打印完了换行后才打印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()

 1 import threading, time, random
 2 share = 4
 3 class MyThread(threading.Thread):
 4     def __init__(self,i):
 5         super().__init__()
 6         self.i = i
 7 
 8     def run(self):
 9         global share
10         for d in range(40):
11             **lock.acquire()**
12             print(share)
13             share += self.i
14             time.sleep(random.random())
15             print("t+", self.i, "=", share)
16             **lock.release()**
17 
18 **lock = threading.Lock()**
19 
20 if __name__ == "__main__":
21     t = MyThread(2)
22     tt = MyThread(6)
23     t.start()
24     # t.join()
25     tt.start()
26 
27 #会看到一下子加2,一下子加6
28 #若将倒数第二行注释去除,则一直加2,完了后才加6
指令锁示例

    2)可重入锁:被某一线程锁定了后还能够被其余线程锁定

    3)有前后顺序的使用---条件变量:threading.Condition对象(建立了一个带锁的线程池), [当具有了某条件后才调用某线程,如生产者-消费者模式]

      定义锁变量:conLock  = threading.Condition()

      得到锁:conLock.acquire()

      通知线程池里其余线程:conLock.notify( [ n=x ] )【唤起x个,默认为1】,clock.notify_all()【唤醒所有】

      释放锁而且进入线程池等待:conLock.wait()

 1 #coding:utf-8
 2 import threading, time
 3 share = 0
 4 **share_condition = threading.Condition()**
 5 
 6 class ProductThread(threading.Thread):
 7     def __init__(self):
 8         super().__init__()
 9         self.name = "生产者"
10 
11     def run(self):
12         global share
13         if share_condition.acquire():
14             while True:
15                 if not share:
16                     share += 1
17                     print( self.name, share )
18                     **share_condition.notify()**       #执行线程池里的一个等待线程
19                 **share_condition.wait()**             #当前线程释放锁并将当前线程放入线程池等待下次notify(),因此若是没有此句则只会显示一句“生产者:1”
20                 time.sleep(1)
21 
22 class CustumerThread(threading.Thread):
23     def __init__(self):
24         super().__init__()
25         self.name = "消费者"
26 
27     def run(self):
28         global share
29         if share_condition.acquire():
30             while True:
31                 if share:
32                     share -= 1
33                     print( self.name, share )
34                     **share_condition.notify()**
35                 **share_condition.wait()**
36                 time.sleep(1)
37 
38 lock = threading.Lock()
39 
40 if __name__ == "__main__":
41     product = ProductThread()
42     customer = CustumerThread()
43     product.start()
44     customer.start()
45 
46 
47 #最后会相继打印:
48 # 生产者 1
49 # 消费者 0
条件变量锁机制示例

    4)部分线程同时使用---信号量:threading.Semaphore对象

      定义锁:sema = threading.Semaphore(n)  #容许同时有n个线程得到锁

      得到锁:sema.acquire()

      释放锁:sema.release()

      经常使用于限制资源的访问,好比数据库的链接池

 1 #coding:utf-8
 2 import threading, time
 3 class MyThread(threading.Thread):
 4     def __init__(self,name):
 5         super().__init__()
 6         self.name = name
 7 
 8     def run(self):
 9         if sema.acquire():
10             print(self.name,"获得了锁")
11             time.sleep(1)
12         sema.release()
13         print(self.name,'释放了锁')
14 
15 sema = threading.Semaphore(2)
16 
17 if __name__ == "__main__":
18     threads = [ MyThread(str(i)+"sema") for i in range(5) ]
19     for thread in threads:
20         thread.start()
21 
22 #输出以下,能够看出同时可以有两个线程得到了锁
23 # 0sema 获得了锁
24 # 1sema 获得了锁
25 # 0sema 释放了锁
26 # 1sema 释放了锁
27 # 2sema 获得了锁
28 # 3sema 获得了锁
29 # 2sema 释放了锁
30 # 4sema 获得了锁
31 # 3sema 释放了锁
32 # 4sema 释放了锁
信号量锁示例

    5)线程间通讯---threading.Event对象(建立了一个带有标志的线程池,当一个线程设置内部标志为True后线程池中的等待线程就会被唤醒并执行)【便是当前执行线程能够决定啥时候(通常是所共同使用的资源已经使用完毕了后)“叫醒”别的线程,须要注意的是,若此时叫醒别人的线程并无wait进入线程池,那么若是其还有代码的话将继续执行下去】

      定义锁变量: event = threading.Event()

      进入线程池等待:event.wait([timeout])

      设置内部标志为True:event.set()  <--->  event.clear()

 1 #coding:utf-8
 2 import threading, time
 3 
 4 class WaitThread(threading.Thread):
 5 
 6     def run(self):
 7         self.name = "等待线程"
 8         print(self.name,"正在等待") #注意此程序的执行顺序,先调用的此线程执行到这里
 9         **event.wait()**               #在此此线程wait进入线程池等待,切换到mainThread直至设置标志
10         print(self.name,"启动了")  #从而此线程在此被唤醒执行
11         **event.clear()**              #然后设置标志为false
12 
13 class MainThread(threading.Thread):
14 
15     def run(self):
16         time.sleep(3)
17         print("主线程更改了标志")
18         **event.set()**
19         print("这里的会在WaitThread打印启动了前打印吗?") 
20 
21 **event = threading.Event()**
22 
23 if __name__ == "__main__":
24     wt = WaitThread()
25     mt = MainThread()
26     wt.start()
27     mt.start()
28 
29 # 输出:
30 # 等待线程 正在等待
31 # 主线程更改了标志
32 # 这里的会在WaitThread打印启动了前打印吗?
33 # 等待线程 启动了
线程间通讯锁示例

    

*补充.关于定时执行:threading.Timer()

 

参考:

《现代操做系统》---Andrew S. Tanebaum

相关文章
相关标签/搜索