python 线程互斥锁Lock(29)

在前一篇文章 python线程建立和传参 中咱们介绍了关于python线程的一些简单函数使用和线程的参数传递,使用多线程能够同时执行多个任务,提升开发效率,可是在实际开发中每每咱们会碰到线程同步问题,假若有这样一个场景:对全局变量累加1000000次,为了提升效率,咱们可使用多线程完成,示例代码以下:python

1git

2程序员

3github

4微信

5多线程

6ide

7函数

8ui

9spa

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

# !usr/bin/env python

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

"""

@Author:何以解忧

@Blog(我的博客地址): shuopython.com

@WeChat Official Account(微信公众号):猿说python

@Github:www.github.com

@File:python_thread_lock.py

@Time:2019/10/17 21:22

 

@Motto:不积跬步无以致千里,不积小流无以成江海,程序人生的精彩须要坚持不懈地积累!

"""

# 导入线程threading模块

import threading

 

# 声明全局变量

g_num = 0

 

def my_thread1():

 

    # 声明全局变量

    global g_num

    # 循环 1000000 次,每次累计加 1

    for i in range(0,1000000):

        g_num = g_num + 1

 

def my_thread2():

 

    # 声明全局变量

    global g_num

    # 循环 1000000 次,每次累计加 1

    for i in range(0,1000000):

        g_num = g_num + 1

 

def main(i):

 

    # 声明全局变量

    global g_num

    # 初始化全局变量,初始值为 0

    g_num = 0

    # 建立两个线程,对全局变量进行累计加 1

    t1 = threading.Thread(target=my_thread1)

    t2 = threading.Thread(target=my_thread2)

 

    # 启动线程

    t1.start()

    t2.start()

    # 阻塞函数,等待线程结束

    t1.join()

    t2.join()

    # 获取全局变量的值

    print("第%d次计算结果:%d "% (i,g_num))

 

if __name__ == "__main__":

 

    # 循环4次,调用main函数,计算全局变量的值

    for i in range(1,5):

        main(i)

输出结果:

1

2

3

4

1次计算结果:1262996

2次计算结果:1661455

3次计算结果:1300211

4次计算结果:1563699

what ? 这是什么操做??看着代码好像也没问题,两个线程,各自累加1000000次,不该该输出是2000000次吗?并且调用了4次main函数,每次输出的结果还不一样!!

 

蹲坑

 

一.线程共享全局变量

分析下上面的代码:两个线程共享全局变量并执行for循环1000000,每次自动加1,咱们都知道两个线程都是同时在运行,也就是说两个线程同时在执行 g_num = g_num + 1 操做, 通过咱们冷静分析一波,貌似结果仍是应该等于2000000,对不对?

 

扎心

 

首先,咱们将上面全局变量自动加 1 的代码分为两步:

1

2

第一步:g_num + 1

第二步:将 g_num + 1 的结果赋值给 g_num

因而可知,执行一个完整的自动加1过程须要两步,然而线程倒是在同时运行,谁也不能保证线程1的第一步和第二步执行完成以后才执行线程2的第一步和第二步,执行的过程充满随机性,这就是致使每次计算结果不一样的缘由所在!

举个简单的例子:

假如当前 g_num 值是100,当线程1执行第一步时,cpu经过计算得到结果101,并准备把计算的结果101赋值给g_num,而后再传值的过程当中,线程2忽然开始执行了而且执行了第一步,此时g_num的值仍未100,101还在传递的过程当中,还没成功赋值,线程2得到计算结果101,并准备传递给g_num,通过一来一去这么一折腾,分明作了两次加 1 操做,g_num结果倒是101,偏差就由此产生,每每循环次数越多,产生的偏差就越大。

 

窒息

 

二.线程互斥锁

为了不上述问题,咱们能够利用线程互斥锁解决这个问题。那么互斥锁究竟是个什么原理呢?互斥锁就比如排队上厕所,一个坑位只能蹲一我的,只有占用坑位的人完事了,另一我的才能上!

上厕所

1.建立互斥锁

导入线程模块,经过 threading.Lock() 建立互斥锁.

1

2

3

4

5

# 导入线程threading模块

import threading

 

# 建立互斥锁

mutex = threading.Lock()

 

2.锁定资源/解锁资源

acquire() — 锁定资源,此时资源是锁定状态,其余线程没法修改锁定的资源,直到等待锁定的资源释放以后才能操做;

release() — 释放资源,也称为解锁操做,对锁定的资源解锁,解锁以后其余线程能够对资源正常操做;

 

以上面的代码为列子:想获得正确的结果,能够直接利用互斥锁在全局变量 加1 以前 锁定资源,而后在计算完成以后释放资源,这样就是一个完整的计算过程,至于应该是哪一个线程先执行,无所谓,先到先得,凭本事说话….演示代码以下:

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

# !usr/bin/env python

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

"""

@Author:何以解忧

@Blog(我的博客地址): shuopython.com

@WeChat Official Account(微信公众号):猿说python

@Github:www.github.com

@File:python_thread_lock.py

@Time:2019/10/18 21:22

 

@Motto:不积跬步无以致千里,不积小流无以成江海,程序人生的精彩须要坚持不懈地积累!

"""

# 导入线程threading模块

import threading

 

# 声明全局变量

g_num = 0

# 建立互斥锁

mutex = threading.Lock()

 

def my_thread1():

 

    # 声明全局变量

    global g_num

    # 循环 1000000 次,每次累计加 1

    for i in range(0,1000000):

        # 锁定资源

        mutex.acquire()

        g_num = g_num + 1

        # 解锁资源

        mutex.release()

 

def my_thread2():

 

    # 声明全局变量

    global g_num

    # 循环 1000000 次,每次累计加 1

    for i in range(0,1000000):

        # 锁定资源

        mutex.acquire()

        g_num = g_num + 1

        # 解锁资源

        mutex.release()

 

def main(i):

 

    # 声明全局变量

    global g_num

    # 初始化全局变量,初始值为 0

    g_num = 0

    # 建立两个线程,对全局变量进行累计加 1

    t1 = threading.Thread(target=my_thread1)

    t2 = threading.Thread(target=my_thread2)

 

    # 启动线程

    t1.start()

    t2.start()

    # 阻塞函数,等待线程结束

    t1.join()

    t2.join()

    # 获取全局变量的值

    print("第%d次计算结果:%d "% (i,g_num))

 

if __name__ == "__main__":

 

    # 循环4次,调用main函数,计算全局变量的值

    for i in range(1,5):

        main(i)

输出结果:

1

2

3

4

1次计算结果:2000000

2次计算结果:2000000

3次计算结果:2000000

4次计算结果:2000000

因而可知,全局变量计算加上互斥锁以后,不论执行多少次,计算结果都相同。注意:互斥锁一旦锁定以后要记得解锁,不然资源会一直处于锁定状态

 

三.线程死锁

1.单个互斥锁的死锁:acquire()/release() 是成对出现的,互斥锁对资源锁定以后就必定要解锁,不然资源会一直处于锁定状态,其余线程没法修改;就比如上面的代码,任何一个线程没有释放资源release(),程序就会一直处于阻塞状态(在等待资源被释放),不信你能够试一试~

2.多个互斥锁的死锁:在同时操做多个互斥锁的时候必定要格外当心,由于一不当心就容易进入死循环,假若有这样一个场景:boss让程序员一实现功能一的开发,让程序员二实现功能二的开发,功能开发完成以后一块儿整合代码!

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

# 导入线程threading模块

import threading

# 导入线程time模块

import time

 

 

# 建立互斥锁

mutex_one = threading.Lock()

mutex_two = threading.Lock()

 

def programmer_thread1():

 

    mutex_one.acquire()

    print("我是程序员1,module1开发正式开始,谁也别动个人代码")

    time.sleep(2)

 

    # 此时会堵塞,由于这个mutex_two已经被线程programmer_thread2抢先上锁了,等待解锁

    mutex_two.acquire()

    print("等待程序员2通知我合并代码")

    mutex_two.release()

 

    mutex_one.release()

 

def programmer_thread2():

    mutex_two.acquire()

    print("我是程序员2,module2开发正式开始,谁也别动个人代码")

    time.sleep(2)

 

    # 此时会堵塞,由于这个mutex_one已经被线程programmer_thread1抢先上锁了,等待解锁

    mutex_one.acquire()

    print("等待程序员1通知我合并代码")

    mutex_one.release()

 

    mutex_two.release()

 

def main():

 

    t1 = threading.Thread(target=programmer_thread1)

    t2 = threading.Thread(target=programmer_thread2)

 

    # 启动线程

    t1.start()

    t2.start()

    # 阻塞函数,等待线程结束

    t1.join()

    t2.join()

    # 整合代码结束

    print("整合代码结束 ")

 

if __name__ == "__main__":

 

    main()

输出结果:

1

2

我是程序员1module1开发正式开始,谁也别动个人代码

我是程序员2module2开发正式开始,谁也别动个人代码

分析下上面代码:程序员1在等程序员2通知,程序员2在等程序员1通知,两个线程都陷入阻塞中,由于两个线程都在等待对方解锁,这就是死锁!因此在开发中对于死锁的问题仍是须要多多注意!

 

四.重点总结

1.线程与线程之间共享全局变量须要设置互斥锁;

2.注意在互斥锁操做中 acquire()/release() 成对出现,避免形成死锁;

 

 

猜你喜欢:

1.python线程建立和传参

2.python函数-缺省参数

3.python局部变量和全局变量

 

转载请注明猿说Python » Python线程互斥锁Lock 

相关文章
相关标签/搜索