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 

  • 相关阅读:
    Leetcode题目94.二叉树的中序遍历(中等)
    Leetcode题目79.单词搜索(回溯+DFS-中等)
    Leetcode题目78.子集(回溯-中等)
    Leetcode题目75.颜色分类(双指针-中等)
    Leetcode题目70.爬楼梯(动态规划+递归-简单)
    Leetcode题目64.最小路径和(动态规划-中等)
    Android 学习笔记之切换主题
    Android学习笔记:摇一摇功能的实现
    百思不得姐demo,只写了视频,图片,和段子,需要的童鞋进
    自己写的音乐播放器Demo开源给需要学习的人,大家编程路上一直进步,
  • 原文地址:https://www.cnblogs.com/guyexiangyun/p/10571437.html
Copyright © 2011-2022 走看看