手把手教你理解python3多线程【2】

本文通过一个Python示例,探讨了多线程环境下全局变量的同步问题。在没有使用锁的情况下,多个线程同时操作同一全局变量导致了混乱的输出结果。引入互斥锁后,线程对全局变量的操作变得有序,确保了数据的一致性和安全性。

写在最前面:写这篇博客的时候一直不能专心,因为今晚淘宝集能量我们和一个队杠上了,生死未卜

下面先看一个栗子:

 

import threading
import time


class ChangeMoney():
    def First(name):
        global money
        for i in range(100):
            money = money+1
            msg = "%s set money to " % name + str(money)
            print(msg)


    def Second(name):
        global money
        for i in range(100):
            money = money+1
            msg = "%s set money to " % name + str(money)
            print(msg)



    def Third(name):
        global money
        for i in range(100):
            money = money+1
            msg = "%s set money to " % name + str(money)
            print(msg)


money = 0
mutex = threading.Lock()

threads = []
t1 = threading.Thread(target=ChangeMoney.First,args=(u'First',))
threads.append(t1)
t2 = threading.Thread(target=ChangeMoney.Second,args=(u'Second',))
threads.append(t2)
t3 = threading.Thread(target=ChangeMoney.Third,args=(u'Third',))
threads.append(t3)

if __name__ == '__main__':
    for t in threads:
        #t = ChangeMoney()
        t.start()
    '''for i in range(5):
        t = ChangeMoney()
        t.start()'''

我们定义了三个线程,同时操作Money这个全局变量,输出结果如下:

First set money to 1
First set money to 2
First set money to 3
First set money to 4
First set money to 5
First set money to 6
First set money to 7
First set money to 8
First set money to 9
First set money to 10
First set money to 11
First set money to 12
First set money to 13
First set money to 14
First set money to 15
First set money to 16
First set money to 17
First set money to 18
First set money to 19
First set money to 20
Second set money to 21
Second set money to 22
Second set money to 23
Second set money to 24
Second set money to 25
Second set money to 26
Second set money to 27
Third set money to 28
Third set money to 29
Third set money to 30
Third set money to 31
Second set money to 32
Second set money to 33
Second set money to 34
Second set money to 35
Second set money to 36
Second set money to 37
Second set money to 38
Third set money to 39
Third set money to 40
Third set money to 41
Third set money to 42
Third set money to 43
Third set money to 44
Second set money to 45
Second set money to 46
Second set money to 47
Second set money to 48
Second set money to 49
Second set money to 50
Third set money to 51
Third set money to 52
Third set money to 53
Third set money to 54
Third set money to 55
Third set money to 56
Third set money to 57
Third set money to 58
Third set money to 59
Third set money to 60

Process finished with exit code 0

可以看到不同线程操作顺序比较混乱,我看到有的博主给的代码可以使线程同时操作结果,像这样:

Thread-5 set num to 2
Thread-3 set num to 3
Thread-2 set num to 5
Thread-1 set num to 5
Thread-4 set num to 4

但是我跑他的代码并没有复现,这里我们姑且认为这么做是不安全的,于是我们引入锁的概念

当多个线程都修改某一个共享数据的时候,需要进行同步控制。

线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。互斥锁为资源引入一个状态:锁定/非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。

加锁

import threading
import time


class ChangeMoney():
    def First(name):
        global money
        for i in range(100):
            if mutex.acquire():
                money = money+1
                msg = "%s set money to " % name + str(money)
                print(msg)
                mutex.release()


    def Second(name):
        global money
        for i in range(100):
            if mutex.acquire():
                money = money+1
                msg = "%s set money to " % name + str(money)
                print(msg)
                mutex.release()


    def Third(name):
        global money
        for i in range(100):
            if mutex.acquire():
                money = money+1
                msg = "%s set money to " % name + str(money)
                print(msg)
                mutex.release()


money = 0
mutex = threading.Lock()

threads = []
t1 = threading.Thread(target=ChangeMoney.First,args=(u'First',))
threads.append(t1)
t2 = threading.Thread(target=ChangeMoney.Second,args=(u'Second',))
threads.append(t2)
t3 = threading.Thread(target=ChangeMoney.Third,args=(u'Third',))
threads.append(t3)

if __name__ == '__main__':
    for t in threads:
        #t = ChangeMoney()
        t.start()

再看输出:

First set money to 1
First set money to 2
First set money to 3
First set money to 4
First set money to 5
First set money to 6
First set money to 7
First set money to 8
First set money to 9
First set money to 10
First set money to 11
First set money to 12
First set money to 13
First set money to 14
First set money to 15
First set money to 16
First set money to 17
First set money to 18
First set money to 19
First set money to 20
Second set money to 21
Second set money to 22
Second set money to 23
Second set money to 24
Second set money to 25
Second set money to 26
Second set money to 27
Second set money to 28
Second set money to 29
Second set money to 30
Second set money to 31
Second set money to 32
Second set money to 33
Second set money to 34
Second set money to 35
Second set money to 36
Second set money to 37
Second set money to 38
Second set money to 39
Second set money to 40
Third set money to 41
Third set money to 42
Third set money to 43
Third set money to 44
Third set money to 45
Third set money to 46
Third set money to 47
Third set money to 48
Third set money to 49
Third set money to 50
Third set money to 51
Third set money to 52
Third set money to 53
Third set money to 54
Third set money to 55
Third set money to 56
Third set money to 57
Third set money to 58
Third set money to 59
Third set money to 60

Process finished with exit code 0

这里我们加了锁以后,对于Money的操作就变得有序起来,目前是数据量较小的情况,一旦数据量变大,线程增多,会出现两个线程同时更改数据的情况,这样是不安全的。

当一个线程调用锁的acquire()方法获得锁时,锁就进入“locked”状态。每次只有一个线程可以获得锁。如果此时另一个线程试图获得这个锁,该线程就会变为“blocked”状态,称为“同步阻塞”。

直到拥有锁的线程调用锁的release()方法释放锁之后,锁进入“unlocked”状态。线程调度程序从处于同步阻塞状态的线程中选择一个来获得锁,并使得该线程进入运行(running)状态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值