zoukankan      html  css  js  c++  java
  • 线程安全与锁的概念

    一个进程包含:运行进程的程序、数据集以及进程控制块。其中进程控制块是保证系统可以进行多任务并发的关键,它控制着进程间的切换活动。

    进程是系统中最小的资源分配单位,而线程是系统中最小的执行单位。多个线程可以共享同一个进程内的数据集,很多情况下,线程访问数据集的速度过快,而数据集的更新却跟不上被访问的速度,导致了访问出现错误。这就是线程安全问题。

     假设有一百个线程同时来操纵一个数,每个线程运行一次减1的操作。代码如下:

     1 import time
     2 import threading
     3 
     4 num = 100
     5 
     6 def redNum():
     7 
     8     global num  # 引用的是全局变量num
     9     num = num - 1  # 对num进行-1操作
    10 
    11 thread_list = [] # 存放多个线程的列表
    12 
    13 for i in range(100):
    14     t = threading.Thread(target=redNum)
    15     t.start()
    16     thread_list.append(t)
    17 
    18 for t in thread_list:  # 阻塞主线程,等待子线程执行完毕
    19     t.join()
    20 
    21 print('num的最终值:', num)

    打印结果:

    num的最终值: 0

    我们获取了想要的结果,这个结果与串行执行程序的结果并无区别。但是当我们加上时间延迟后,情况就变得不一样了:

     1 import time
     2 import threading
     3 
     4 num = 100
     5 
     6 def redNum():
     7 
     8     global num  
     9 
    10     temp = num # 将num存入临时变量
    11     time.sleep(0.000001) # 设置时间延迟
    12     num = temp - 1  
    13 
    14 thread_list = [] 
    15 
    16 for i in range(100):
    17     t = threading.Thread(target=redNum)
    18     t.start()
    19     thread_list.append(t)
    20 
    21 for t in thread_list: 
    22     t.join()
    23 
    24 print('num的最终值:', num)

    打印结果并不为0:

    num的最终值: 91

    如果重新运行程序,就会发现每次打印出的数字都会有所不同。

    为什么最终的结果不是0呢?

    如上图所示,每个线程都在访问num这个变量。但是由于我们在线程内部设置了时间延迟,所以读取num的速度要远大于在线程内部处理num的速度,所以很多线程获取的依然是未作修改的num值。子线程访问num的速度快修改后将num重新写入的速度慢,导致了所谓的线程不安全——当某个线程访问共享数据时,未对这次访问进行保护,其他线程仍有机会访问该数据,出现了数据前后不一致的情况。例如我们在网上买火车票,信息显示余票还有100张,这时A访问了网站并购买了一张票,在网站处理A的购买信息时,B也访问了网站。如果网站没有对A的访问进行保护,就会出现B看到余票仍有100张的情况。而实际上,余票只有99张了。

     那么怎样才能使线程安全呢?这就要用到“锁”了。

    我们可以为某一段代码加上“锁”,加上“锁”后,每次只允许一个线程运行这段代码,也就是每个时间只能有一个线程访问共享数据。在python中,这种限制线程访问的锁称为“同步锁”。可以用threading.Lock()来实例化一个同步锁对象。

     1 import time
     2 import threading
     3 
     4 num = 100
     5 
     6 lock = threading.Lock() # 实例化一个Lock对象,该对象是一个同步锁
     7 
     8 def redNum():
     9 
    10     global num
    11 
    12     lock.acquire() # 为下面的运行代码加上同步锁
    13     temp = num 
    14     time.sleep(0.000001) 
    15     num = temp - 1
    16     lock.release() # 释放同步锁
    17 
    18 thread_list = []
    19 
    20 for i in range(100):
    21     t = threading.Thread(target=redNum)
    22     t.start()
    23     thread_list.append(t)
    24 
    25 for t in thread_list:
    26     t.join()
    27 
    28 print('num的最终值:', num)

    这时,多线程的程序获得的结果就是我们期望的了:

    num的最终值: 0

    当在每个线程内部使用lock.acquire()与lock.release()时,这两个方法间的代码相当于串行执行。每个时刻都只能有1个线程运行这两个方法间的代码。当某个线程内的锁被释放后,其他线程才能去竞争这把“锁”,获得锁的线程才能被执行。

    参考博客:www.cnblogs.com/yuanchenqi/articles/6248025.html 

  • 相关阅读:
    27. Remove Element
    列表变成字典
    1. Two Sum
    CVPR2019:What and How Well You Performed? A Multitask Learning Approach to Action Quality Assessment
    959. Regions Cut By Slashes
    118. Pascal's Triangle
    loj3117 IOI2017 接线 wiring 题解
    题解 NOI2019 序列
    题解 省选联考2020 组合数问题
    题解 Educational Codeforces Round 90 (Rated for Div. 2) (CF1373)
  • 原文地址:https://www.cnblogs.com/guyexiangyun/p/10571437.html
Copyright © 2011-2022 走看看