孤叶翔云

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

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

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

 假设有一百个线程同时来操纵一个数,每个线程运行一次减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 

posted on 2019-03-21 18:58  孤叶翔云  阅读(393)  评论(0编辑  收藏  举报