线程

根据学进程的节奏,接下来该学锁了。线程也有锁,也分为互斥锁和 递归锁。线程锁较进程锁使用的更为广泛。

首先我先解释一下死锁

1.死锁:

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

举个经典的例子看一下吧  科学家吃面的问题

互斥锁(一般锁)

noodle_lock = Lock()
fork_lock = Lock()
def eat1(name):
    fork_lock.acquire()
    print('%s拿到叉子了'%name)
    noodle_lock.acquire()
    print('%s拿到面了'%name)
    print('%s吃面'%name)
    fork_lock.release()
    noodle_lock.release()
def eat2(name):
    noodle_lock.acquire()
    print('%s拿到面了'%name)
    time.sleep(1)
    fork_lock.acquire()
    print('%s拿到叉子了'%name)
    print('%s吃面'%name)
    noodle_lock.release()
    fork_lock.release()
Thread(target=eat1,args=('ming',)).start()
Thread(target=eat2,args=('hong',)).start()
Thread(target=eat1,args=('lan',)).start()
Thread(target=eat2,args=('huang',)).start()

运行结果:

                                             

python threading 线程锁 python线程锁有几种锁_python

解释:

首先面条锁 和  叉子锁是公共的且只有一把   ming吃饭积极 拿完叉子 拿面一气呵成 开心的吃了一口面
而 hong 拿到面之后 有点懵 就在这时候 lan把叉子拿到了 并且 面和叉子只有一份  两人互不相让 导致谁也吃不了,导致程序处于挂起状态----也就是著名的死锁。

递归锁

noodle_lock = fork_lock = RLock()
def eat1(name):
    fork_lock.acquire()
    print('%s拿到叉子了'%name)
    noodle_lock.acquire()
    print('%s拿到面了'%name)
    print('%s吃面'%name)
    fork_lock.release()
    noodle_lock.release()
def eat2(name):
    noodle_lock.acquire()
    print('%s拿到面了'%name)
    time.sleep(1)
    fork_lock.acquire()
    print('%s拿到叉子了'%name)
    print('%s吃面'%name)
    noodle_lock.release()
    fork_lock.release()
Thread(target=eat1,args=('ming',)).start()
Thread(target=eat2,args=('hong',)).start()
Thread(target=eat1,args=('lan',)).start()
Thread(target=eat2,args=('huang',)).start()

大家可能发现了,就只有定义锁的时候不一样。其次特别注意的是,定义锁的时候要像我这样两个叠在一起定义,如果像这样分别定义  noodle_lock = RLock()    fork_lock = RLock() ,就定义成两个递归锁了

  运行结果:

    

python threading 线程锁 python线程锁有几种锁_python_02

  解释:

大家可以看到一个人拿到叉子必拿到面(或者拿到面必拿到叉子) 这时候的递归锁 相当于一个钥匙串  拿到了一把也就相当于拿到了整个钥匙串 在这里可以理解为 叉子和面条绑在了一起  拿到了其中一样东西 ,其他进程就争夺不到了

再给大家用一个图片深入一下递归锁的概念,因为进程中没出现过所以有些难懂。

                                                   

python threading 线程锁 python线程锁有几种锁_死锁_03

2.信号量

目前对于信号量的理解 我是有一点疑义的。但我先保留我的意见,把信号量看成可以进行多个线程访问数据的功能。

举一个简单的例子吧:

# 要实现的功能:4个人可以同时进入房间取数据
sem = Semaphore(4)
def room(sem,a,b):
    sem.acquire()
    print(a+b)
    time.sleep(2)  # 这里的睡两秒是为了增强信号量的效果   想象他们执行完后 等待两秒才归还钥匙
    sem.release()
for i in range(10):
    Thread(target=room,args=(sem,i,i+1)).start()

  运行结果:

python threading 线程锁 python线程锁有几种锁_死锁_04

解释:

结果是每4个每4个出现,其中有不按顺序出现,说明是异步。

3.事件

尽管写代码有些枯燥,但它能很好的证明我们想看到的结果。我就是从一个个代码的小例子理解每个概念的。

下面我们写一个关于事件的小例子,线程的事件和进程的事件是一样的,所以这算是个复习吧。希望大家能够和我一样,不要眼高手低,有时候代码敲10遍你会了,可是敲11遍的时候,你就有了新的理解。

# 用线程的事件来实现数据库连接的情况
 e = Event()
 def check(e):
     time.sleep(random.randint(0,3))
     e.set()

 def connet(e):
     count = 0
     while count <3:
         e.wait(0.5)     # wait 里面放参数指的是 等待几秒
         if e.is_set():
             print('成功连接数据库')
             break
         else:
             print('第%s次连接失败'%count)
             count += 1
     else:       
        print('数据库连接失败')
 t1 = Thread(target=connet, args=(e,)).start()
 t2 = Thread(target=check, args=(e,)).start()

解释:

e.wait(0.5) 先不去判断状态而是先等待0.5秒,过后再判断状态如果是True,则通信。  如果是False,则阻塞。