Skip to content

Latest commit

 

History

History
275 lines (201 loc) · 6.09 KB

File metadata and controls

275 lines (201 loc) · 6.09 KB

线程同步(锁)

一、线程共享数据

多线程和多进程最主要的区别在于:多进程中同一个变量,会被拷贝到每一个进程中,互不影响

而多线程中,所有的变量都是线程间共享的。所以,任意一个变量都可以被线程进行修改,原因就是在于线程中变量共享的。

代码实例

import threading
import time
name = 'lucky'

def fun1():
    global name
    name = '张三'
    print(threading.current_thread().name, name)
    time.sleep(2)

def fun2():
    print(threading.current_thread().name, name)

if __name__ == '__main__':
    t1 = threading.Thread(target=fun1)
    t2 = threading.Thread(target=fun2)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(name)
    print('over')
    
# ---结果为---
# Thread-1 张三
# Thread-2 张三
# 张三
# over

!> 从结果可以看出线程之间的数据是共享的

二、线程间共享数据的问题(数据错乱)

  • 代码实例
import threading

i = 1
def change_num(num):
    global i
    i += num
    i -= num

def fun1():
    for i in range(10000000):
        change_num(6)

def fun2():
    for i in range(10000000):
        change_num(8)

if __name__ == '__main__':
    t1 = threading.Thread(target=fun1)
    t2 = threading.Thread(target=fun2)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(i)
    print('over')

# ---结果---
# 第一次执行
# fun1---------- -1181950
# fun2------- -2083066

# 第二次执行
# fun1---------- -183358
# fun2------- -944143

从上述代码可以看出,两个线程同时对同一数据进行读写就会产生数据的错乱。

解决办法:在进行写的时候保证只有一个线程

三、线程锁(Lock,Rlock)

3.1、Lock(互斥锁)

lock锁是线程模块中的一个类,有俩个主要的方法:acquire() 和release() ,当调用acquire()时,它进行锁定并阻塞执行,直到调用release() ,以防止数据损坏,因为一次只有一个线程进行访问资源。

  • 作用

    避免线程冲突

  • 注意

    1. 当前的线程锁定以后,后面的线程会等待 (线程等待/线程阻塞)
    2. 需要release以后才能解锁恢复正常
    3. 不能重复锁定
  • 代码实例
import threading, time
lock = threading.Lock()
i = 1
def fun1():
    global i
    if lock.acquire(): # 上锁
        for x in range(1000000):
            i += x
            i -= x
        lock.release() # 解锁
    print('fun1----------', i)

def fun2():
    global i
    if lock.acquire(): # 上锁
        for x in range(10000000):
            i += x
            i -= x
        lock.release() # 解锁
    print('fun2-------', i)

if __name__ == '__main__':
    t1 = threading.Thread(target=fun1)
    t2 = threading.Thread(target=fun2)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(i)
  • lock的简写
with lock
    for x in range(10000000):
        i += x
        i -= x
print('fun2-------', i)

死锁现象

所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。

此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁

from threading import Thread,Lock
import time
 
mutexA = Lock()
mutexB = Lock()
 
class MyThread(Thread):
    def run(self):
        self.task1()
        self.task2()
 
 
    def task1(self):
        mutexA.acquire()
        print('%s task1 get A' %self.name)
 
        mutexB.acquire()
        print('%s task1 get B' % self.name)
        mutexB.release()
 
        mutexA.release()
 
    def task2(self):
        mutexB.acquire()
        print('%s task2 get B' % self.name)
        time.sleep(1)  # Thread-2 拿到执行权,执行get A出现死锁,此时thread2需要B锁,而thread1占用,与此同时,thread1需要A锁,thread2占用
        mutexA.acquire()
        print('%s task2 get A' % self.name)
 
        mutexA.release()
        mutexB.release()
 
 
if __name__ == '__main__':
    for i in range(10):
        t = MyThread()
        t.start()
 
-------------------输出
Thread-1 task1 get A
Thread-1 task1 get B
Thread-1 task2 get B
Thread-2 task1 get A # 出现死锁,整个程序被阻塞

解决方法:递归锁

3.2、RLock (递归锁)

在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。

这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。

直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁,二者的区别是:递归锁可以连续acquire多次,而互斥锁只能acquire一次

  • Rlock简单使用

    from threading import Thread,RLock
    import time
     
    mutexA = mutexB = RLock()
     
    class MyThread(Thread):
        def run(self):
            self.task1()
            self.task2()
     
     
        def task1(self):
            mutexA.acquire()
            print('%s task1 get A' %self.name)
     
            mutexB.acquire()
            print('%s task1 get B' % self.name)
            mutexB.release()
     
            mutexA.release()
            time.sleep(1) # Thread-2 拿到执行权,,此时counter=0,thread2执行task1
     
        def task2(self):
            mutexB.acquire()
            print('%s task2 get B' % self.name)
     
            mutexA.acquire()
            print('%s task2 get A' % self.name)
     
            mutexA.release()
            mutexB.release()
     
     
    if __name__ == '__main__':
        for i in range(10):
            t = MyThread()
            t.start()
     
     
    ------------------------输出
     
    Thread-1 task1 get A
    Thread-1 task1 get B
    Thread-2 task1 get A
    Thread-2 task1 get B
    Thread-3 task1 get A
    Thread-3 task1 get B
    Thread-4 task1 get A
    Thread-4 task1 get B
    Thread-5 task1 get A
    Thread-5 task1 get B
  • rlock的简写

    rlock = threading.RLock()
    with rlock:
    	pass