python 線程條件變量Condition(31)

對於線程與線程之間的交互我們在前面的文章已經介紹了 python 互斥鎖Lock / python事件Event , 今天繼續介紹一種線程交互方式 – 線程條件變量Condition.

 

帥醒

 

一.線程條件變量Condition相關函數介紹

acquire() —  線程鎖,注意線程條件變量Condition中的所有相關函數使用必須在acquire() /release() 內部操作;

release() — 釋放鎖,注意線程條件變量Condition中的所有相關函數使用必須在acquire() /release() 內部操作;

wait(timeout) —  線程掛起(阻塞狀態),直到收到一個notify通知或者超時纔會被喚醒繼續運行(超時參數默認不設置,可選填,類型是浮點數,單位是秒)。wait()必須在已獲得Lock前提下才能調用,否則會觸發RuntimeError;

notify(n=1) —  通知其他線程,那些掛起的線程接到這個通知之後會開始運行,缺省參數,默認是通知一個正等待通知的線程,最多則喚醒n個等待的線程。notify()必須在已獲得Lock前提下才能調用,否則會觸發RuntimeError,notify()不會主動釋放Lock;

notifyAll() —  如果wait狀態線程比較多,notifyAll的作用就是通知所有線程;

 

不要臉

 

 

二.線程條件變量Condition原理

在前面的文章已經介紹過互斥鎖,主要作用是並行訪問共享資源時,保護共享資源,防止出現髒數據。python 條件變量Condition也需要關聯互斥鎖,同時Condition自身提供了wait/notify/notifyAll方法,用於阻塞/通知其他並行線程,可以訪問共享資源了。可以這麼理解,Condition提供了一種多線程通信機制,假如線程1需要數據,那麼線程1就阻塞等待,這時線程2就去製造數據,線程2製造好數據後,通知線程1可以去取數據了,然後線程1去獲取數據。

張飛

三.線程條件變量Condition使用

案例一:成語接龍

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

# !usr/bin/env python

# -*- coding:utf-8 _*-

"""

@Author:何以解憂

@Blog(個人博客地址): shuopython.com

@WeChat Official Account(微信公衆號):猿說python

@Github:www.github.com

@File:python_.py

@Time:2019/10/21 21:25

 

@Motto:不積跬步無以至千里,不積小流無以成江海,程序人生的精彩需要堅持不懈地積累!

"""

 

# 導入線程模塊

import threading

 

# 創建條件變量condition

con = threading.Condition()

 

def thread_one(name):

    # 條件變量condition 線程上鎖

    con.acquire()

 

    print("{}:成語接龍準備好了嗎".format(name))

    # 喚醒正在等待(wait)的線程

    con.notify()

 

    # 等待對方迴應消息,使用wait阻塞線程,等待對方通過notify喚醒本線程

    con.wait()

    print("{}:一乾二淨".format(name))

    # 喚醒對方

    con.notify()

 

    # 等待消息答應

    con.wait()

    print("{}:一天就知道看抖音美女,給你來個簡單點的,來了:毛手毛腳".format(name))

    # 喚醒對方

    con.notify()

 

    # 等待消息答應

    con.wait()

    print("{}:喲喲喲,不錯不錯!".format(name))

    # 喚醒對方

    con.notify()

 

    # 條件變量condition 線程釋放鎖

    con.release()

 

def thread_two(name):

    # 條件變量condition 線程上鎖

    con.acquire()

 

    # wait阻塞狀態,等待其他線程通過notify喚醒本線程

    con.wait()

    print("{}:準備好了~開始吧!".format(name))

    # 喚醒對方

    con.notify()

 

    # 等待消息答應

    con.wait()

    print("{}:淨你妹啊,沒法接...來個簡單點的...".format(name))

    # 喚醒對方

    con.notify()

 

    # 等待消息答應

    con.wait()

    print("{}:嘿,這個我知道:腳踏實地".format(name))

    # 喚醒對方

    con.notify()

 

    con.release()

 

if __name__ == "__main__":

 

    # 創建並初始化線程

    t1 = threading.Thread(target=thread_one,args=("A"))

    t2 = threading.Thread(target=thread_two,args=("B"))

 

    # 啓動線程 -- 注意線程啓動順序,啓動順序很重要

    t2.start()

    t1.start()

 

    # 阻塞主線程,等待子線程結束

    t1.join()

    t2.join()

 

 

    print("程序結束!")

輸出結果:

1

2

3

4

5

6

7

8

A:成語接龍準備好了嗎

B:準備好了~開始吧!

A:一乾二淨

B:淨你妹啊,沒法接...來個簡單點的...

A:一天就知道看抖音美女,給你來個簡單點的,來了:毛手毛腳

B:,這個我知道:腳踏實地

A:喲喲喲,不錯不錯!

程序結束!

思考

案例二:生產者與消費者模式,以吃火鍋爲例:一盤老肉片有10塊肉,吃完了又重新往鍋里加….

生產者:往鍋里加老肉片,每次加一盤(10塊);

消費者:吃煮熟的肉片,沒吃一片,肉片數量減一,吃完爲止;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

# 導入線程模塊

import threading

import time

 

# 創建條件變量condition

con = threading.Condition()

meat_num = 0

 

def thread_consumers():

    # 條件變量condition 線程上鎖

    con.acquire()

    

    # 全局變量聲明關鍵字 global

    global meat_num

    meat_num = 0

 

    # 等待肉片下鍋煮熟

    con.wait()

    while True:

        print("我來一塊肉片...")

        meat_num -= 1

        print("剩餘肉片數量:%d"%meat_num)

        time.sleep(0.5)

        if meat_num == 0:

            # 肉片吃光了,通知老闆添加肉片

            print("老闆,再來一份老肉片...")

            con.notify()

            # 肉片吃光了,等待肉片

            con.wait()

 

    # 條件變量condition 線程釋放鎖

    con.release()

 

 

def thread_producer():

    # 條件變量condition 線程上鎖

    con.acquire()

    # 全局變量聲明關鍵字 global

    global meat_num

 

    # 肉片熟了,可以開始吃了

    meat_num = 10

    print("肉片熟了,可以開始吃了...")

    con.notify()

    while True:

        # 阻塞函數,等待肉片吃完的通知

        con.wait()

        meat_num = 10

        # 添加肉片完成,可以繼續開吃

        print("添加肉片成功!當前肉片數量:%d"%meat_num)

        time.sleep(1)

        con.notify()

 

    con.release()

 

 

if __name__ == "__main__":

    # 創建並初始化線程

    t1 = threading.Thread(target=thread_producer)

    t2 = threading.Thread(target=thread_consumers)

 

    # 啓動線程 -- 注意線程啓動順序,啓動順序很重要

    t2.start()

    t1.start()

 

    # 阻塞主線程,等待子線程結束

    t1.join()

    t2.join()

 

    print("程序結束!")

輸出結果:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

肉片熟了,可以開始吃了...

我來一塊肉片...

剩餘肉片數量:9

我來一塊肉片...

剩餘肉片數量:8

我來一塊肉片...

剩餘肉片數量:7

我來一塊肉片...

剩餘肉片數量:6

我來一塊肉片...

剩餘肉片數量:5

我來一塊肉片...

剩餘肉片數量:4

我來一塊肉片...

剩餘肉片數量:3

我來一塊肉片...

剩餘肉片數量:2

我來一塊肉片...

剩餘肉片數量:1

我來一塊肉片...

剩餘肉片數量:0

老闆,再來一份老肉片...

添加肉片成功!當前肉片數量:10

我來一塊肉片...

剩餘肉片數量:9

我來一塊肉片...

剩餘肉片數量:8

我來一塊肉片...

剩餘肉片數量:7

.............

注意:

1.全局變量要聲明關鍵字 global

2.注意線程的啓動順序,這個很重要;

 

四.重點總結

注意線程互斥鎖Lock/線程事件Event/線程條件變量Condition三者的區別,場景不同,使用方式也不同,前兩者一般可以作爲簡單的線程交互,線程條件變量Condition可以用於比較複雜的線程交互!

猜你喜歡:

1.python線程創建和參數傳遞

2.python線程互斥鎖Lock

3.python線程事件Event

4.python return邏輯判斷表達式

 

轉載請註明猿說Python » python條件變量Condition


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章