同步

同步的概念

同步就是协同步调,按预定的先后次序进行运行。如:你说完,我再说。"同"字从字面上容易理解为一起动作,其实不是,"同"字应是指协同、协助、互相配合。如进程、线程同步,可理解为进程或线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B执行,再将结果给A;A再继续操作。

解决线程同时修改全局变量的方式

对于上一篇中(多线程共享全局变量)的那个计算错误的问题,可以通过线程同步来进行解决。

思路,如下:

  1. 系统调用t1,然后获取到g_num的值为0,此时上一把锁,即不允许其他线程操作g_num
  2. t1对g_num的值进行+1
  3. t1解锁,此时g_num的值为1,其他的线程就可以使用g_num了,而且是g_num的值不是0而是1
  4. 同理其他线程在对g_num进行修改时,都要先上锁,处理完后再解锁,在上锁的整个过程中不允许其他线程访问,就保证了数据的正确性

互斥锁

当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制。线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。

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

某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。

threading模块中定义了Lock类,可以方便的处理锁定:

# 创建锁
mutex = threading.Lock() # 锁定
mutex.acquire() # 释放
mutex.release()

注意:

  • 如果这个锁之前是没有上锁的,那么acquire不会堵塞
  • 如果在调用acquire对这个锁上锁之前 它已经被 其他线程上了锁,那么此时acquire会堵塞,直到这个锁被解锁为止

使用互斥锁完成2个线程对同一个全局变量各加9999999 次的操作

import threading
import time g_num = 0
# 创建一个互斥锁,默认为未上锁zhuangtai
mutex = threading.Lock() def test1(num):
global g_num for i in range(num): mutex.acquire()
g_num += 1
mutex.release()
print("--test1, g_num = %d--" % g_num) def test2(num):
global g_num for i in range(num): mutex.acquire()
g_num += 1
mutex.release() print("--test2, g_num = %d--" % g_num) if __name__ == "__main__": print("--创建线程之前, g_num = %d--" % g_num) t1 = threading.Thread(target=test1, args=(9999999,))
t1.start() t2 = threading.Thread(target=test2, args=(9999999,))
t2.start() while len(threading.enumerate()) != 1:
time.sleep(1) print("最终结果为:g_num=%d" % g_num)

运行结果:

--创建线程之前, g_num = 0--
--test1, g_num = 19580251--
--test2, g_num = 19999998--
最终结果为:g_num=19999998

可以看到最后的结果,加入互斥锁后,其结果与预期相符。

上锁解锁过程

当一个线程调用锁的acquire()方法获得锁时,锁就进入“locked”状态。

每次只有一个线程可以获得锁。如果此时另一个线程试图获得这个锁,该线程就会变为“blocked”状态,称为“阻塞”,直到拥有锁的线程调用锁的release()方法释放锁之后,锁进入“unlocked”状态。

线程调度程序从处于同步阻塞状态的线程中选择一个来获得锁,并使得该线程进入运行(running)状态。

总结

锁的好处:

  • 确保了某段关键代码只能由一个线程从头到尾完整地执行

锁的坏处:

  • 阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了
  • 由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁

死锁

在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。

尽管死锁很少发生,但一旦发生就会造成应用的停止响应。下面看一个死锁的例子

import threading
import time class MyThread1(threading.Thread): def run(self): # 对mutex1上锁
mutex1.acquire() # mutex1上锁后,延迟1秒,等待另外一个线程,把mutex2上锁
print(self.name + " up")
time.sleep(1) # 此时这里会堵塞,因为mutex2已经被另外的线程抢先上锁了
mutex2.acquire()
print(self.name + " down")
mutex2.release() # 对mutex1解锁
mutex1.release() class MyThread2(threading.Thread): def run(self): # 对mutex2上锁
mutex2.acquire() # mutex2上锁后,延迟1秒,等待另外一个线程,把mutex1上锁
print(self.name + " up")
time.sleep(1) # 此时这里会堵塞,因为mutex1已经被另外的线程抢先上锁了
mutex1.acquire()
print(self.name + " down")
mutex1.release() # 对mutex2解锁
mutex2.release() mutex1 = threading.Lock()
mutex2 = threading.Lock() if __name__ == "__main__": t1 = MyThread1()
t1.start() t2 = MyThread2()
t2.start()

运行结果:

Thread-1 up
Thread-2 up

此时已经进入到了死锁状态,可以使用ctrl-c退出

避免死锁

  • 程序设计时要尽量避免(银行家算法)
  • 添加超时时间等

python同步、互斥锁、死锁的更多相关文章

  1. python线程互斥锁Lock(29)

    在前一篇文章 python线程创建和传参 中我们介绍了关于python线程的一些简单函数使用和线程的参数传递,使用多线程可以同时执行多个任务,提高开发效率,但是在实际开发中往往我们会碰到线程同步问题, ...

  2. Python Threading 线程/互斥锁/死锁/GIL锁

    导入线程包 import threading 准备函数线程,传参数 t1 = threading.Thread(target=func,args=(args,)) 类继承线程,创建线程对象 class ...

  3. Linux 线程编程2.0——线程同步-互斥锁

    当我们需要控制对共享资源的存取的时候,可以用一种简单的加锁的方法来控制.我们可以创建一个读/写程序,它们共用一个共享缓冲区,使用互斥锁来控制对缓冲区的存取. 函数 pthread_mutex_init ...

  4. 并发编程~~~多线程~~~守护线程, 互斥锁, 死锁现象与递归锁, 信号量 (Semaphore), GIL全局解释器锁

    一 守护线程 from threading import Thread import time def foo(): print(123) time.sleep(1) print('end123') ...

  5. go同步互斥锁

    import "sync" var ( myMap = make(map[int]int, 10) lock sync.Mutex //声明一个全局的互斥锁 //sync 包 同步 ...

  6. C++11 多线程同步 互斥锁 条件变量

    在多线程程序中,线程同步(多个线程访问一个资源保证顺序)是一个非常重要的问题,Linux下常见的线程同步的方法有下面几种: 互斥锁 条件变量 信号量 这篇博客只介绍互斥量和条件变量的使用. 互斥锁和条 ...

  7. Python的互斥锁与信号量

    并发与锁 a. 多个线程共享数据的时候,如果数据不进行保护,那么可能出现数据不一致现象,使用锁,信号量.条件锁 b. c.互斥锁1. 互斥锁,是使用一把锁把代码保护起来,以牺牲性能换取代码的安全性,那 ...

  8. Python多线程同步互斥锁

    接着上篇多线程继续讲,上篇最后的多线程共享全局变量对变量的处理值出错在本文中给出解决方案. 出现这个情况的原因是在python解释器中GIL全局解释器锁. GIL:全局解释器锁,每个线程在执行的过程都 ...

  9. Python 锁 同步 互斥锁

    import time from threading import Lock,Thread num = 100 def f1(loc): loc.acquire() global num tmp = ...

  10. Python多线程互斥锁

    import threading import time num=0 def Mylock(lock): global num lock.acquire() num=num+1 time.sleep( ...

随机推荐

  1. 有关wkwebview和UIwebview获取html中的标签方法

    wkwebview方法如下: [webView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id r ...

  2. HashTable使用举例--C#

    一,哈希表(Hashtable)简述 在.NET Framework中,Hashtable是System.Collections命名空间提供的一个容器,用于处理和表现类似keyvalue的键值对,其中 ...

  3. ASP.NET Core MVC 源码学习:详解 Action 的匹配

    前言 在 上一篇 文章中,我们已经学习了 ASP.NET Core MVC 的启动流程,那么 MVC 在启动了之后,当请求到达过来的时候,它是怎么样处理的呢? 又是怎么样把我们的请求准确的传达到我们的 ...

  4. TempData ViewBag ViewData区别

    在这篇<MVC 5使用TempData Object跨视图传递数据>https://www.cnblogs.com/insus/p/3378016.html中,已经在评论回复网友:网上查找 ...

  5. mysql5.5 五种日期

    mysql(5.5)所支持的日期时间类型有:DATETIME. TIMESTAMP.DATE.TIME.YEAR. 几种类型比较如下: 日期时间类型 占用空间 日期格式 最小值 最大值 零值表示  D ...

  6. Java语言中的面向对象特性

    面向对象的基本特征 1.封装性 封装性就是把对象的属性和服务结合成一个独立的相同单位,并尽可能隐蔽对象的内部细节,包含两个含义: ◇ 把对象的全部属性和全部服务结合在一起,形成一个不可分割的独立单位( ...

  7. Android X 相关汇总

    一.说明 官方原文如下: We hope the division between android.* and androidx.* makes it more obvious which APIs ...

  8. Numpy学习四:numpy.power()用法

    numpy.power(n, x) 对数组n的元素分别求x次方.x可以是数字,也可以是数组,但是n和x的列数要相同.

  9. 微信小程序-form表单-获取用户输入文本框的值

    微信小程序-form表单-获取用户输入文本框的值 <input name='formnickname' class="textarea" placeholder=" ...

  10. 使用Java面向对象单词必备

    第一章 class   班级,用声明类 object     目标,整个程序集对大 static  静态的 final  不可更改的,用声明常量 private  私有的,用访问权限 public  ...