您的位置:首页 > 其它

day 0807 多线程—共享全局变量问题

2019-08-08 18:35 148 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/DAVID11234/article/details/98882809

多线程—共享全局变量问题

多线程可能遇到的问题

假设有两个线程t1,t2假设都要对一个全局变量g_num进行运算,两个线程t1和t2分别对g_num各加10次,g_num的最终结果

import threading
import time
g_num=0
def work1(num):
global g_num
for i in range(num):
g_num+=1
print('---in work1,g_num is %d---'% g_num)
def work2(num):
global g_num
for i in range(num):
g_num+=1
print('---in work2,g_num is %d---'% g_num)
print('----线程创建之前g_num:%d'% g_num)
t1=threading.Thread(target=work1,args=(10,))
t2=threading.Thread(target=work2,args=(10,))
t1.start()
t2.start()
while len(threading.enumerate()) !=1:
time.sleep(1)
print('2个线程对同一个变量的最终结果:',g_num)

----线程创建之前g_num:0
—in work1,g_num is 10—
—in work2,g_num is 20—
2个线程对同一个变量的最终结果: 20

  • 理论上来讲

在num=0时,t1取得num=0,此时系统把t1调度为‘sleeping’的状态,t2准换为‘running’的状态,t2也获得num=0。然后,t2对得到的值进行加1并赋给num。num=1,然后系统又将t2调度为‘sleeping’的状态,把t1转换为‘running‘,线程t1又把它之前得到的0加1后赋值给num.这种情况,明明俩线程都完成了一个次工作,但结果还是num=1

  • 实际上

因为电脑运行速度过快,t2还来不及与t1进行线程切换,t1就运行完了,如果将10改为1000000,会发现

----线程创建之前g_num:0
—in work1,g_num is 1144345—
—in work2,g_num is 1195423—
2个线程对同一个变量的最终结果: 1195423

两条线程由于共享资源,会对资源产生竞争,导致数据结果不正确

同步

同步,就是协同步调,按照预定的先后次序进行运行。好比说相声,一个说完,另外一个人再说。

进程和线程同步,可以理解为进程或者线程A和B一块配黑,A执行一定程度时需要依赖B的某个结果,于是停下来,让B运行,B开始运行,再将结果给A,A再继续操作,如此往复,直至程序结束。

计算错误的解决

通过’线程同步’来进行解决

思路:

  • 系统调度t1,获取num=0,此时上一把锁,即不允许其他线程操作num
  • 对num的值加1
  • 解锁,此时num的值为1,其他的线程就可以使用num了,此时num=1
  • 同理,其他线程也先上锁,处理完后再解锁。不允许其他线程访问,保证数据的正确性。

互斥锁

当多个线程几乎同时修改某个共享数据时,需要进行同步控制。

线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制就是引入互斥锁。

互斥锁为我们的资源引入一个状态: 锁定/非锁定

某个线程要更改共享数据时,先将其锁定,此时资源状态为锁定,其他线程不能对其更改;直到该线程释放资源,资源状态变为’非锁定‘状态,其他线程才能再次锁定该资源。

互斥锁,保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。

在threading模块里,定义了Lock()类,可以方便的处理锁定。

mutex = threading.Lock() #创建
mutex.acquire([blocking]) #锁定
mutex.release()#释放
  • blocking True,则当前线程堵塞,直到获取这个锁为止(如未指定,则默认为True)
  • False,则当前线程不被堵塞,
import threading
import time
g_num=0
def work1(num):
global g_num
for i in range(num):
if mutex.acquire(True):
g_num+=1
mutex.release()
print('---in work1,g_num is %d---'% g_num)
def work2(num):
global g_num
for i in range(num):
if mutex.acquire(True):
g_num += 1
mutex.release()
print('---in work2,g_num is %d---'% g_num)
print('----线程创建之前g_num:%d'% g_num)
mutex=threading.Lock()
t1=threading.Thread(target=work1,args=(1000000,))
t2=threading.Thread(target=work2,args=(1000000,))
t1.start()
t2.start()
while len(threading.enumerate()) !=1:
time.sleep(1)
print('2个线程对同一个变量的最终结果:',g_num)

----线程创建之前g_num:0
—in work1,g_num is 1917539—
—in work2,g_num is 2000000—
2个线程对同一个变量的最终结果: 2000000

死锁

在线程间共享多个资源时,如果两个线程分别占有一部分资源,并且同时等待对方的资源,就会造成死锁。说相声两个人都等对面的先说话然后都不说话,就很尴尬。

死锁一般很少发生,但一旦发生就会造成应用停止响应。

生产消费者问题

也就是有限缓冲问题,是一个多线程同步的经典案例

描述一个两个固定大小缓冲区的线程-即所谓的“生产者”和消费者 在实际运行时会发生的问题。

生产者的主要作用,生成一定量的数据放在缓冲区中,然后,重复此过程。与此同时,消费者也在缓冲区消耗这些数据

整个问题的关键是,生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区空时消耗数据。

解决办法
要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。通常采用进程间通信的方法解决该问题,常用的方法有信号灯法等。如果解决方法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。该问题也能被推广到多个生产者和消费者的情形

1.队列,先进先出

2.栈,先进后出

Python中queue(py3)(py2,Queue),模块提供了一个同步的,线程安全的队列类,包括先入先出(FIFO)队列Queue,和后入先出(LIFO)队列LifoQueue和优先级队列PriorityQueue.

这些队列实现了锁原语(原子操作,要么不做,要么做完),可以在线程中直接使用

可以使用队列来实现线程间的同步

FIFO队列实现生产者消费者问题


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: