8.同步锁lock

在以下函数中,如果想要将参数c减到0,函数需要执行100次

def a():
    global c
    c-=1
c=100

这样太慢,100次函数执行得出结果太慢,就可以同时创建100个线程,1次减完,代码如下:

thread_list=[]
for i in range(100):
    t=threading.Thread(Target=a)
    t.start()
    thread_list.append(t)
for i in thread_list:
    t.join()
print('执行结束,最后c=%d'%c)

以上代码是可以完成需求的,能够开出100个线程同时对数据减一

但是如果将函数a进行修改,如下:最终返回的数据就是跟预想的有偏差的,执行结果就不是0

def a():
    global c
    time.sleep(4)
    c-=1
c=100
thread_list=[]
for i in range(100):
    t=threading.Thread(target=a)
    t.start()
    thread_list.append(t)
for i in thread_list:
    t.join()
print('执行结束,最后c=%d'%c)

原因:

A线程首先获取全局变量c的值,如a=100,然后sleep函数时,a线程进入阻塞状态,cpu切换到其他线程,
同样要获取c的值,因为A线程还未执行到-1代码,c的值还是100,线程b获得的c值就是100,然后就进入阻塞状态
这时a线程执行完毕了,c的值变成99,A线程结束。然后b线程对c=100执行-1操作,c的值也是99。
这样100个线程下来,就会使最后的结果不是0
这种现象被称为线程不安全,线程不同步

解决方法:

1.join   

加join后该线程的代码不执行完,不会执行其他线程,所写的多线程就变成串行执行,没有意义

2.加同步锁

方法   声明一个同步锁对象 l=threading.Lock()
    同步锁的开头 l.acquire()
    释放同步锁 l.release()

在同步锁中的程序全部串行执行,但是同步锁外的程序可以继续按照多线程执行,可以在数据处理的关键位置添加同步锁

def a():
    global c
    l.acquire()
    time.sleep(0.05)
    c-=1          #给减一操作添加同步锁
    l.release()
c=100
thread_list=[]
l=threading.Lock()
for i in range(100):
    t=threading.Thread(target=a)
    t.start()
    thread_list.append(t)
for i in thread_list:
    t.join()
print('执行结束,最后c=%d'%c)

为什么已经给解释器加过GIL锁了,我们的程序还要添加同步锁?

GIL保证在同一时刻只有一个线程进入解释器中
同步锁是限制解释器来回切换线程,将解释器在某段时间内固定在一个线程,防止冲突。

posted @ 2020-10-28 20:53  maday  阅读(78)  评论(0)    收藏  举报