1. 程式人生 > >python3多執行緒----鎖機制

python3多執行緒----鎖機制

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Version: python 3.6.3
# Tools: Pycharm 2017.3.3

__date__ = '2018/7/20 9:49'
__author__ = 'cdl'

import time
import threading

"""執行緒中的鎖機制"""

"""
有兩個執行緒A和B,A和B裡的程式都加了同一個鎖物件,當執行緒A率先執行到lock.acquire()(拿到全域性唯一的鎖後).
執行緒B只能等到執行緒A釋放鎖lock.release()後(歸還鎖)才能執行lock.acquire()(拿到全域性唯一的鎖)並執行後面的程式碼
"""
# 使用鎖
lock = threading.Lock()  # 生成鎖物件,全域性唯一
lock.acquire()  # 獲取鎖。未獲取到的執行緒會阻塞程式,直到獲取到鎖才會往下執行
lock.release()  #  釋放鎖,歸回鎖,其他執行緒可以拿去用了
# 注:lock.acquire() 和 lock.release()必須成對出現。否則就有可能造成死鎖

# 為了避免出現死鎖情況,推薦使用上下文管理器來加鎖
lock = threading.Lock()
with lock:        # with語句會在這個程式碼塊執行前自動獲取鎖,在執行結束後自動釋放鎖
    # 這裡寫自己的程式碼
    pass
# 使用鎖的意義? -----------加鎖是為了對鎖內資源(變數)進行鎖定,避免其他執行緒篡改已被鎖定的資源,以達到我們預期的效果。
# 對比不用鎖的執行緒
# def job1():
#     global n
#     for i in range(10):
#         n += 1
#         print('job1', n)
# def job2():
#     global n
#     for i in range(20):
#         n += 10
#         print('job2', n)
#
# n = 0
# threading_01 = threading.Thread(target=job1)
# threading_02 = threading.Thread(target=job2)
# threading_01.start()
# threading_02.start()
# 加鎖之後進行對比
def job1():
    global n, lock
    lock.acquire()  # 獲取鎖
    for i in range(10):
        n += 1
        print('job1', n)
    lock.release()

def job2():
    global n, lock
    lock.acquire()  # 獲取鎖
    for i in range(10):
        n += 10
        print('job2', n)
    lock.release()
n = 0
lock = threading.Lock()  # 生成鎖物件,全域性唯一
threading_01 = threading.Thread(target=job1)
threading_02 = threading.Thread(target=job2)
threading_01.start()
threading_02.start()
# 有時候在同一個執行緒中,我們可能會多次請求同一資源(就是,獲取同一鎖鑰匙),俗稱鎖巢狀
def main():
    n = 0
    lock = threading.Lock()
    with lock:
        for i in range(10):
            n += 1
            with lock:  # 第二次獲取鎖時,發現鎖已經被同一執行緒的人拿走了。自己也就理所當然,拿不到鎖,程式就卡住了。
                print(n)

t1 = threading.Thread(target=main)
t1.start()
# 可重入鎖RLock,專門來處理這個問題
def num():
    n = 0
    # 生成可重入鎖物件
    lock = threading.RLock()
    with lock:
        for i in range(10):
            n += 1
            with lock:
                print(n)

t1 = threading.Thread(target=num)
t1.start()
# 防止死鎖的加鎖機制
"""
出現情況:
同一執行緒,巢狀獲取同把鎖,造成死鎖
多個執行緒,不按順序同時獲取多個鎖。造成死鎖
具體解釋:
執行緒1,巢狀獲取A,B兩個鎖,執行緒2,巢狀獲取B,A兩個鎖。 
由於兩個執行緒是交替執行的,是有機會遇到執行緒1獲取到鎖A,而未獲取到鎖B,在同一時刻,執行緒2獲取到鎖B,而未獲取到鎖A。
由於鎖B已經被執行緒2獲取了,所以執行緒1就卡在了獲取鎖B處,由於是巢狀鎖,執行緒1未獲取並釋放B,是不能釋放鎖A的。
這是導致執行緒2也獲取不到鎖A,也卡住了。兩個執行緒,各執一鎖,各不讓步,造成死鎖。
"""
import threading
from contextlib import contextmanager

# Thread-local state to stored information on locks already acquired
_local = threading.local()

@contextmanager
def acquire(*locks):
    # Sort locks by object identifier
    locks = sorted(locks, key=lambda x: id(x))

    # Make sure lock order of previously acquired locks is not violated
    acquired = getattr(_local,'acquired',[])
    if acquired and max(id(lock) for lock in acquired) >= id(locks[0]):
        raise RuntimeError('Lock Order Violation')

    # Acquire all of the locks
    acquired.extend(locks)
    _local.acquired = acquired
    try:
        for lock in locks:
            lock.acquire()
        yield
    finally:
        # Release locks in reverse order of acquisition
        for lock in reversed(locks):
            lock.release()
        del acquired[-len(locks):]


x_lock = threading.Lock()
y_lock = threading.Lock()
def thread_1():
    while True:
        with acquire(x_lock):
            with acquire(y_lock):
                print('Thread-1')

def thread_2():
    while True:
        with acquire(y_lock):
            with acquire(x_lock):
                print('Thread-2')
t1 = threading.Thread(target=thread_1)
t1.daemon = True
t1.start()
t2 = threading.Thread(target=thread_2)
t2.daemon = True
t2.start()