Python多线程操作之互斥锁、递归锁、信号量、事件实例详解

本文实例讲述了Python多线程操作之互斥锁、递归锁、信号量、事件。分享给大家供大家参考,具体如下:

互斥锁:

  • 为什么要有互斥锁:由于多线程是并行的,如果某一线程取出了某一个数据将要进行操作,但它还没有那么快执行完操作,这时候如果另外一个线程也要操作这个数据,那么这个数据可能会因为两次操作而发生错误
import time,threading

x=6
def run1():
  print("run1我拿到了数据:",x)
  print("我现在还不想操作,先睡一下")
  time.sleep(3)
  print("再看一下数据,稳一稳",x)

def run2():
  global x
  print("run2我拿到了数据:", x)

  x=5
  print(x)

t1=threading.Thread(target=run1)
t2=threading.Thread(target=run2)

t1.start()
t2.start()
t1.join()
t2.join()

  • 而多线程的互斥锁机制本质上是:申请一个锁,A线程拿了钥匙【acquire】之后,如果B也想拿到钥匙是不行的,只有等A把钥匙还回来【release】才行
  • 如何使用互斥锁:
    1. 定义一个锁对象:锁对象=threading.Lock()
    2. 请求锁:锁对象.acquire()
    3. 释放锁:锁对象.release()

使用互斥锁来更改上段代码

import time,threading

x=6
def run1():
  lock.acquire()
  global x
  print("run1我拿到了数据,x=",x)
  print("我现在还不想操作,先睡一下")
  time.sleep(3)
  print("再看一下数据,稳一稳,x=",x)
  x+=1
  print("run1操作完毕:x=",x)
  lock.release()
def run2():
  lock.acquire()
  global x
  print("run2我拿到了数据:", x)
  x+=1
  print("run2操作完毕:x=",x)
  lock.release()

lock=threading.Lock()#生成一个锁对象
t1=threading.Thread(target=run1)
t2=threading.Thread(target=run2)

t1.start()
t2.start()
start_time=time.time()
t1.join()
t2.join()
print("最终的x=",x)
print(time.time()-start_time)#3.0多说明,由于受到锁的影响,run2要等待run1释放lock,所以变成了串行

这种互斥锁在操作系统中可以称作“临界区”,如果想了解更多:

https://baike.baidu.com/item/%E4%B8%B4%E7%95%8C%E5%8C%BA/8942134?fr=aladdin


递归锁:

  • 为什么要有递归锁:互斥锁本质上是阻止其他线程进入,如果有两个需要阻止其他线程进入的操作【像两个人过独木桥】,那么需要两个锁,而想要锁上第二个如果直接用第一个锁的acquire会失败,因为第一个锁还没release,我们可以选择再定义一个互斥锁对象来acquire,但这仅仅是两层的情况下,如果多层的吧,那么就需要定义好几个互斥锁对象了【而且由于对象变多,有时候会因为互相调用锁而发生死锁】。递归锁就是为了处理这种情况,递归锁对象允许多次acquire和多次release

    • 发生死锁的情况[A拿到A锁,想要拿B锁,B拿着B锁,想要A锁]

【以过独木桥为例】:桥只能容一个人通过,A只能看得到北边桥上有没有人,看不到南边桥有没有人,当他看到北边桥没人就会过桥,等到他到桥中间才能看到南边桥有没有人,B情况相反:【于是当两个人一起过桥的时候就会发生死锁】

import threading,time

"""
A只能看得到北边桥上有没有人,看不到南边桥有没有人,
当他看到北边桥没人就会过桥,等到他到桥中间才能看到南边桥有没有人
"""
def A():
  lockNorth.acquire()#拿到北边桥的锁
  print("A过桥北")
  time.sleep(3)#过桥中
  lockSorth.acquire()#企图过到南边桥,
  print("A过桥南")
  time.sleep(3) # 过桥中
  lockSorth.release()
  lockNorth.release()
  print("A过桥成功")

"""
B只能看得到南边桥上有没有人,看不到北边桥有没有人,
当他看到南边桥没人就会过桥,等到他到桥中间才能看到北边桥有没有人
"""
def B():
  lockSorth.acquire() # 企图过到南边桥,
  print("B过桥南")
  time.sleep(3) # 过桥中
  lockNorth.acquire() # 拿到北边桥的锁
  print("B过桥北")
  time.sleep(3) # 过桥中
  lockNorth.release()
  lockSorth.release()
  print("B过桥成功")

lockNorth=threading.Lock()
lockSorth=threading.Lock()

tA=threading.Thread(target=A)
tB=threading.Thread(target=B)
tA.start()
tB.start()

tA.join()
tB.join()

  • 递归锁的本质是:本质上还是一个锁,但如果在一个线程里面可以多次acquire。【因为只有一个锁,所以不会发生互相调用的死锁,而因为可以多次调用,所以可以锁多次】
  • 如何使用递归锁:
    1. 定义一个锁对象:递归锁对象=threading.RLock()
    2. 请求锁:锁对象.acquire()
    3. 释放锁:锁对象.release()

使用递归锁来解决上面的死锁问题:

import threading,time

"""
A只能看得到北边桥上有没有人,看不到南边桥有没有人,
当他看到北边桥没人就会过桥,等到他到桥中间才能看到南边桥有没有人
"""
def A():
  lock.acquire()#拿到北边桥的锁
  print("A过桥北")
  time.sleep(3)#过桥中
  lock.acquire()#企图过到南边桥,
  print("A过桥南")
  time.sleep(3) # 过桥中
  lock.release()
  lock.release()
  print("A过桥成功")

"""
B只能看得到南边桥上有没有人,看不到北边桥有没有人,
当他看到南边桥没人就会过桥,等到他到桥中间才能看到北边桥有没有人
"""
def B():
  lock.acquire() # 拿南桥锁,
  print("B过桥南")
  time.sleep(3) # 过桥中
  lock.acquire() # 企图拿北桥的锁
  print("B过桥北")
  time.sleep(3) # 过桥中
  lock.release()
  lock.release()
  print("B过桥成功")

lock=threading.RLock()

tA=threading.Thread(target=A)
tB=threading.Thread(target=B)
tA.start()
tB.start()

tA.join()
tB.join()

【由于本质是一把锁,A拿到锁后,B要等待】


信号量:

  • 什么是信号量:

信号量可以限制进入的线程的数量。

  • 如何使用信号量:
    1. 创建信号量对象:信号量对象=threading.BoundedSemaphore(x),x是限制进程的数量
    2. 当有进程需要进入的时候,调用acquire()来减少信号量:信号量对象.acquire()
    3. 当有进程离开的时候,调用release()来增加信号量:信号量对象.release()
import threading,time

def run():
  s.acquire()
  print("hello")
  time.sleep(1.5)
  s.release()

s=threading.BoundedSemaphore(3)#限制3个

threading_list=[]
for i in range(12):#创建12个线程
  obj=threading.Thread(target=run)
  obj.setDaemon(True) # 设置守护线程,避免干扰主线程运行,并行等待
  obj.start()

for i in range(4):
  print("")#为了把结果分割,可以清楚看出分为了三组
  time.sleep(1.5)
#结果分为三组是因为运行的太快了,三个线程装入的时间差太小


事件:

  • 什么是事件:当发生线程发生一件事的时候如果要提醒另外一个线程,使用事件。双方共享该事件对象【等待的一方会阻塞而进行等待】,当一方更改事件对象的时候,另外一方也能知道【以读者-写者为例:读者要等写者告诉他去读才会去读,写者写完后要设置一个事件,当该事件设置时,读者就会来读】
  • 如何使用事件:
    1. 创建事件对象:事件对象=threading.Event()
    2. 设置事件:事件对象.set()    判断事件是否set:事件对象.is_set(),等待事件set:事件对象.wait()
    3. 清除事件:事件对象.clear()
import threading,time

def read():
  while True:
    if event.is_set():
      print("事件已设置,我要读了!!!!")
      time.sleep(1)
    else:#事件未设置
      print("还没写好,我要等咯")
      event.wait()#那么就等着咯
      #如果等到了
      print("终于等到了!那么我又可以读了")
      time.sleep(1)

def write():
  event.clear()#初始设空
  while True:
    time.sleep(3)#写
    event.set()#设置事件,一旦set,那么读者wait就有返回了,读者可以继续运行了
    print("write:写好了")
    time.sleep(2)#等人读
    event.clear()#清除事件

event=threading.Event() #创建事件对象

t1=threading.Thread(target=write)
t2=threading.Thread(target=read)

t1.start()
t2.start()
t1.join()
t2.join()

"""结果显示:读者确实一直在等待写者写好"""

更多关于Python相关内容感兴趣的读者可查看本站专题:《Python进程与线程操作技巧总结》、《Python数据结构与算法教程》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》、《Python入门与进阶经典教程》、《Python+MySQL数据库程序设计入门教程》及《Python常见数据库操作技巧汇总》

希望本文所述对大家Python程序设计有所帮助。

时间: 2020-03-21

Python多线程编程(四):使用Lock互斥锁

前面已经演示了Python:使用threading模块实现多线程编程二两种方式起线程和Python:使用threading模块实现多线程编程三threading.Thread类的重要函数,这两篇文章的示例都是演示了互不相干的独立线程,现在我们考虑这样一个问题:假设各个线程需要访问同一公共资源,我们的代码该怎么写? 复制代码 代码如下: ''' Created on 2012-9-8   @author: walfred @module: thread.ThreadTest3 '''  impor

Python 多线程不加锁分块读取文件的方法

多线程读取或写入,一般会涉及到同步的问题,否则产生的结果是无法预期的.那么在读取一个文件的时候,我们可以通过加锁,但读不像写操作,会导致文件错误,另外锁操作是有一定的耗时.因此通过文件分块,可以比较有效的解决多线程读问题,之前看到有人写的分块操作,比较复杂,需要实现建立好线程以及所读取块信息,在这里,我提供了一种比较简便的方法,以供参考. #!/user/bin/env python #_*_coding:utf-8_*_ from threading import Thread import

Python多线程编程(五):死锁的形成

前一篇文章Python:使用threading模块实现多线程编程四[使用Lock互斥锁]我们已经开始涉及到如何使用互斥锁来保护我们的公共资源了,现在考虑下面的情况– 如果有多个公共资源,在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,这会引起什么问题? 死锁概念 所谓死锁: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程.

Python的互斥锁与信号量详解

并发与锁 多个线程共享数据的时候,如果数据不进行保护,那么可能出现数据不一致现象,使用锁,信号量.条件锁 互斥锁 1. 互斥锁,是使用一把锁把代码保护起来,以牺牲性能换取代码的安全性,那么Rlock后 必须要relase 解锁 不然将会失去多线程程序的优势 2. 互斥锁的基本使用规则: import threading # 声明互斥锁 lock=threading.Rlock(); def handle(sid):# 功能实现代码 lock.acquire() #加锁 # writer code

Python多线程编程(六):可重入锁RLock

考虑这种情况:如果一个线程遇到锁嵌套的情况该怎么办,这个嵌套是指当我一个线程在获取临界资源时,又需要再次获取. 根据这种情况,代码如下: 复制代码 代码如下: ''' Created on 2012-9-8   @author: walfred @module: thread.ThreadTest6 '''    import threading  import time    counter = 0  mutex = threading.Lock()    class MyThread(thr

python多线程高级锁condition简单用法示例

本文实例讲述了python多线程高级锁condition简单用法.分享给大家供大家参考,具体如下: 多线程编程中如果使用Condition对象代替lock, 能够实现在某个事件触发后才处理数据, condition中含有的方法: - wait:线程挂起,收到notify通知后继续运行 - notify:通知其他线程, 解除其它线程的wai状态 - notifyAll(): 通知所有线程 - acquire和release: 获得锁和解除锁, 与lock类似, - enter和exit使得对象支持

Python实现的多线程同步与互斥锁功能示例

本文实例讲述了Python实现的多线程同步与互斥锁功能.分享给大家供大家参考,具体如下: #! /usr/bin/env python #coding=utf-8 import threading import time ''' #1.不加锁 num = 0 class MyThread(threading.Thread): def run(self): global num time.sleep(1) #一定要sleep!!! num = num + 1 msg = self.name + '

Python多线程编程之多线程加锁操作示例

本文实例讲述了Python多线程编程之多线程加锁操作.分享给大家供大家参考,具体如下: Python语言本身是支持多线程的,不像PHP语言. 下面的例子是多个线程做同一批任务,任务总是有task_num个,每次线程做一个任务(print),做完后继续取任务,直到所有任务完成为止. # -*- coding:utf-8 -*- #! python2 import threading start_task = 0 task_num = 10000 mu = threading.Lock() ###通

详解python多线程、锁、event事件机制的简单使用

线程和进程 1.线程共享创建它的进程的地址空间,进程有自己的地址空间 2.线程可以访问进程所有的数据,线程可以相互访问 3.线程之间的数据是独立的 4.子进程复制线程的数据 5.子进程启动后是独立的 ,父进程只能杀掉子进程,而不能进行数据交换 6.修改线程中的数据,都是会影响其他的线程,而对于进程的更改,不会影响子进程 threading.Thread Thread 是threading模块中最重要的类之一,可以使用它来创建线程.有两种方式来创建线程:一种是通过继承Thread类,重写它的run

对python多线程中Lock()与RLock()锁详解

资源总是有限的,程序运行如果对同一个对象进行操作,则有可能造成资源的争用,甚至导致死锁 也可能导致读写混乱 锁提供如下方法: 1.Lock.acquire([blocking]) 2.Lock.release() 3.threading.Lock() 加载线程的锁对象,是一个基本的锁对象,一次只能一个锁定,其余锁请求,需等待锁释放后才能获取 4.threading.RLock() 多重锁,在同一线程中可用被多次acquire.如果使用RLock,那么acquire和release必须成对出现,

Python3.X 线程中信号量的使用方法示例

前言 最近在学习python,发现了解线程信号量的基础知识,对深入理解python的线程会大有帮助.所以本文将给大家介绍Python3.X线程中信号量的使用方法,下面话不多说,来一起看看详细的介绍: 方法示例 线程中,信号量主要是用来维持有限的资源,使得在一定时间使用该资源的线程只有指定的数量 # -*- coding:utf-8 -*- """ Created by FizLin on 2017/07/23/-下午10:59 mail: https://github.com

对python多线程中互斥锁Threading.Lock的简单应用详解

一.线程共享进程资源 每个线程互相独立,相互之间没有任何关系,但是在同一个进程中的资源,线程是共享的,如果不进行资源的合理分配,对数据造成破坏,使得线程运行的结果不可预期.这种现象称为"线程不安全". 实例如下: #-*- coding: utf-8 -*- import threading import time def test_xc(): f = open("test.txt","a") f.write("test_dxc&quo

对python 多线程中的守护线程与join的用法详解

多线程:在同一个时间做多件事 守护线程:如果在程序中将子线程设置为守护线程,则该子线程会在主线程结束时自动退出,设置方式为thread.setDaemon(True),要在thread.start()之前设置,默认是false的,也就是主线程结束时,子线程依然在执行. thread.join():在子线程完成运行之前,该子线程的父线程(一般就是主线程)将一直存在,也就是被阻塞 实例: #!/usr/bin/python # encoding: utf-8 import threading fro

对Python 多线程统计所有csv文件的行数方法详解

如下所示: #统计某文件夹下的所有csv文件的行数(多线程) import threading import csv import os class MyThreadLine(threading.Thread): #用于统计csv文件的行数的线程类 def __init__(self,path): threading.Thread.__init__(self) #父类初始化 self.path=path #路径 self.line=-1 #统计行数 def run(self): reader =

python基础知识(一)变量与简单数据类型详解

1.1变量 变量的命名规则: 1.只能包含字母.数字.下划线,且不能用数字开头 2.不能使用python关键字 3.简短且具有描述性 1.2字符串 python中用引号引起来的都是字符串,单引号双引号都可以 a.字符串的索引 s = 'ABCDEF' #索引 s1 = s[0] s2 = s[-1] s3 = s[0:4] print(s1) #A print(s2) #F print(s3) #ABC,左闭右开 #打印全部 s4 = s[:] #s[0:] print(s4) s5 = s[0

C#中实现线程同步lock关键字的用法详解

1. lock关键字保证一个代码块在执行的过程中不会受到其他线程的干扰,这是通过在该代码块的运行过程中对特定的对象加互斥锁来实现的. 2. lock关键字的参数必须是引用类型的对象.lock对基本数据类型如int,long等无效,因为它所作用的类型必须是对象.如果传入long类型数据,势必被转换为Int64结构类型,则加锁的是全新的对象引用.如果需要对它们进行互斥访问限制,可以使用System.Threading.Interlocked类提供的方法,这个类是提供原子操作的. 3. lock(th

linux中各种锁机制的使用与区别详解

前言: 相信需要了解这方面的知识的小伙伴,已经基本对进程间通信和线程间通信有了一定了解.例如,进程间通信的机制之一:共享内存(在这里不做详解):多个进程可同时访问同一块内存.如果不对访问这块内存的临界区进行互斥或者同步,那么进程的运行很可能出现一些不可预知的错误和结果. 接下来我们了解三种常见的Linux下的互斥操作->锁. 1.互斥锁(mutex) 特点:对于读者和写者来说.只要有一方获取了锁,另一方则不能继续获取,进而执行临界区代码. 创建锁: 有两种方法创建互斥锁,静态方式和动态方式.PO

Python多线程中阻塞(join)与锁(Lock)使用误区解析

关于阻塞主线程 join的错误用法 Thread.join() 作用为阻塞主线程,即在子线程未返回的时候,主线程等待其返回然后再继续执行. join不能与start在循环里连用 以下为错误代码,代码创建了5个线程,然后用一个循环激活线程,激活之后令其阻塞主线程. threads = [Thread() for i in range(5)] for thread in threads: thread.start() thread.join() 执行过程: 1. 第一次循环中,主线程通过start函

python线程中同步锁详解

在使用多线程的应用下,如何保证线程安全,以及线程之间的同步,或者访问共享变量等问题是十分棘手的问题,也是使用多线程下面临的问题,如果处理不好,会带来较严重的后果,使用python多线程中提供Lock Rlock Semaphore Event Condition 用来保证线程之间的同步,后者保证访问共享变量的互斥问题 Lock & RLock:互斥锁 用来保证多线程访问共享变量的问题 Semaphore对象:Lock互斥锁的加强版,可以被多个线程同时拥有,而Lock只能被某一个线程同时拥有. E