python线程中同步锁详解

在使用多线程的应用下,如何保证线程安全,以及线程之间的同步,或者访问共享变量等问题是十分棘手的问题,也是使用多线程下面临的问题,如果处理不好,会带来较严重的后果,使用python多线程中提供Lock Rlock Semaphore Event Condition 用来保证线程之间的同步,后者保证访问共享变量的互斥问题

Lock & RLock:互斥锁 用来保证多线程访问共享变量的问题
Semaphore对象:Lock互斥锁的加强版,可以被多个线程同时拥有,而Lock只能被某一个线程同时拥有。
Event对象: 它是线程间通信的方式,相当于信号,一个线程可以给另外一个线程发送信号后让其执行操作。
Condition对象:其可以在某些事件触发或者达到特定的条件后才处理数据

1、Lock(互斥锁)

请求锁定 — 进入锁定池等待 — 获取锁 — 已锁定 — 释放锁

Lock(指令锁)是可用的最低级的同步指令。Lock处于锁定状态时,不被特定的线程拥有。Lock包含两种状态——锁定和非锁定,以及两个基本的方法。

可以认为Lock有一个锁定池,当线程请求锁定时,将线程至于池中,直到获得锁定后出池。池中的线程处于状态图中的同步阻塞状态。

构造方法:
Lock()

实例方法:
acquire([timeout]): 使线程进入同步阻塞状态,尝试获得锁定。
release(): 释放锁。使用前线程必须已获得锁定,否则将抛出异常。

if mutex.acquire():
 counter += 1
 print "I am %s, set counter:%s" % (self.name, counter)
  mutex.release()

2、RLock(可重入锁)

RLock(可重入锁)是一个可以被同一个线程请求多次的同步指令。RLock使用了“拥有的线程”和“递归等级”的概念,处于锁定状态时,RLock被某个线程拥有。拥有RLock的线程可以再次调用acquire(),释放锁时需要调用release()相同次数。

可以认为RLock包含一个锁定池和一个初始值为0的计数器,每次成功调用 acquire()/release(),计数器将+1/-1,为0时锁处于未锁定状态。

构造方法:
RLock()

实例方法:
acquire([timeout])/release(): 跟Lock差不多。

3、Semaphore(共享对象访问)

咱们再聊聊Semaphore ,说实话Semaphore是我最晚使用的同步锁,以前类似的实现,是我用Rlock实现的,相对来说有些绕,毕竟Rlock 是需要成对的锁定和开锁的》。。。

Semaphore管理一个内置的计数器,
每当调用acquire()时内置计数器-1;
调用release() 时内置计数器+1;
计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。

直接上代码,我们把semaphore控制为3,也就是说,同时有3个线程可以用这个锁,剩下的线程也之只能是阻塞等待了…

#coding:utf-8
#blog xiaorui.cc
import time
import threading

semaphore = threading.Semaphore(3)

def func():
 if semaphore.acquire():
  for i in range(3):
   time.sleep(1)
   print (threading.currentThread().getName() + '获取锁')
  semaphore.release()
  print (threading.currentThread().getName() + ' 释放锁')

for i in range(5):
 t1 = threading.Thread(target=func)
 t1.start()

4、Event(线程间通信)

Event内部包含了一个标志位,初始的时候为false。
可以使用使用set()来将其设置为true;
或者使用clear()将其从新设置为false;
可以使用is_set()来检查标志位的状态;

另一个最重要的函数就是wait(timeout=None),用来阻塞当前线程,直到event的内部标志位被设置为true或者timeout超时。如果内部标志位为true则wait()函数理解返回。

import threading
import time

class MyThread(threading.Thread):
 def __init__(self, signal):
  threading.Thread.__init__(self)
  self.singal = signal

 def run(self):
  print "I am %s,I will sleep ..."%self.name
  self.singal.wait()
  print "I am %s, I awake..." %self.name

if __name__ == "__main__":
 singal = threading.Event()
 for t in range(0, 3):
  thread = MyThread(singal)
  thread.start()

 print "main thread sleep 3 seconds... "
 time.sleep(3)

 singal.set()

5、Condition(线程同步)

可以把Condition理解为一把高级的琐,它提供了比Lock, RLock更高级的功能,允许我们能够控制复杂的线程同步问题。threadiong.Condition在内部维护一个琐对象(默认是RLock),可以在创建Condigtion对象的时候把琐对象作为参数传入。Condition也提供了acquire, release方法,其含义与琐的acquire, release方法一致,其实它只是简单的调用内部琐对象的对应的方法而已。Condition还提供了如下方法(特别要注意:这些方法只有在占用琐(acquire)之后才能调用,否则将会报RuntimeError异常。):

Condition.wait([timeout]):

wait方法释放内部所占用的琐,同时线程被挂起,直至接收到通知被唤醒或超时(如果提供了timeout参数的话)。当线程被唤醒并重新占有琐的时候,程序才会继续执行下去。

Condition.notify():

唤醒一个挂起的线程(如果存在挂起的线程)。注意:notify()方法不会释放所占用的琐。

Condition.notify_all()
Condition.notifyAll()

唤醒所有挂起的线程(如果存在挂起的线程)。注意:这些方法不会释放所占用的琐。

对于Condition有个例子,大家可以观摩下。

from threading import Thread, Condition
import time
import random

queue = []
MAX_NUM = 10
condition = Condition()

class ProducerThread(Thread):
 def run(self):
  nums = range(5)
  global queue
  while True:
   condition.acquire()
   if len(queue) == MAX_NUM:
    print "Queue full, producer is waiting"
    condition.wait()
    print "Space in queue, Consumer notified the producer"
   num = random.choice(nums)
   queue.append(num)
   print "Produced", num
   condition.notify()
   condition.release()
   time.sleep(random.random())

class ConsumerThread(Thread):
 def run(self):
  global queue
  while True:
   condition.acquire()
   if not queue:
    print "Nothing in queue, consumer is waiting"
    condition.wait()
    print "Producer added something to queue and notified the consumer"
   num = queue.pop(0)
   print "Consumed", num
   condition.notify()
   condition.release()
   time.sleep(random.random())

ProducerThread().start()
ConsumerThread().start()

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

时间: 2018-04-25

Python多线程同步---文件读写控制方法

1.实现文件读写的文件ltz_schedule_times.py #! /usr/bin/env python #coding=utf-8 import os def ReadTimes(): res = [] if os.path.exists('schedule_times.txt'): fp = open('schedule_times.txt', 'r') else: os.system('touch schedule_times.txt') fp = open('schedule_ti

python多线程同步实例教程

前言 进程之间通信与线程同步是一个历久弥新的话题,对编程稍有了解应该都知道,但是细说又说不清.一方面除了工作中可能用的比较少,另一方面就是这些概念牵涉到的东西比较多,而且相对较深.网络编程,服务端编程,并发应用等都会涉及到.其开发和调试过程都不直观.由于同步通信机制的原理都是想通的,本文希通过望借助python实例来将抽象概念具体化. 阅读之前可以参考之前的一篇文章:python多线程与多进程及其区别,了解一下线程和进程的创建. python多线程同步 python中提供两个标准库thread和

Python多线程同步Lock、RLock、Semaphore、Event实例

一.多线程同步 由于CPython的python解释器在单线程模式下执行,所以导致python的多线程在很多的时候并不能很好地发挥多核cpu的资源.大部分情况都推荐使用多进程. python的多线程的同步与其他语言基本相同,主要包含: Lock & RLock :用来确保多线程多共享资源的访问. Semaphore : 用来确保一定资源多线程访问时的上限,例如资源池.  Event : 是最简单的线程间通信的方式,一个线程可以发送信号,其他的线程接收到信号后执行操作. 二.实例 1)Lock &a

Python多线程实现同步的四种方式

临界资源即那些一次只能被一个线程访问的资源,典型例子就是打印机,它一次只能被一个程序用来执行打印功能,因为不能多个线程同时操作,而访问这部分资源的代码通常称之为临界区. 锁机制 threading的Lock类,用该类的acquire函数进行加锁,用realease函数进行解锁 import threading import time class Num: def __init__(self): self.num = 0 self.lock = threading.Lock() def add(s

Python多线程编程(七):使用Condition实现复杂同步

目前我们已经会使用Lock去对公共资源进行互斥访问了,也探讨了同一线程可以使用RLock去重入锁,但是尽管如此我们只不过才处理了一些程序中简单的同步现象,我们甚至还不能很合理的去解决使用Lock锁带来的死锁问题.所以我们得学会使用更深层的解决同步问题. Python提供的Condition对象提供了对复杂线程同步问题的支持.Condition被称为条件变量,除了提供与Lock类似的acquire和release方法外,还提供了wait和notify方法. 使用Condition的主要方式为:线程

详解python多线程之间的同步(一)

引言: 线程之间经常需要协同工作,通过某种技术,让一个线程访问某些数据时,其它线程不能访问这些数据,直到该线程完成对数据的操作.这些技术包括临界区(Critical Section),互斥量(Mutex),信号量(Semaphore),事件Event等. Event threading库中的event对象通过使用内部一个flag标记,通过flag的True或者False的变化来进行操作.      名称                                      含义 set( )

python线程中的同步问题及解决方法

多线程开发可能遇到的问题 假设两个线程t1和t2都要对num=0进行增1运算,t1和t2都各对num修改1000000次,num的最终的结果应该为2000000.但是由于是多线程访问,有可能出现下面情况: from threading import Thread import time num = 0 def test1(): global num for i in range(1000000): num += 1 print("--test1--num=%d" % num) def

深入解析Python中的线程同步方法

同步访问共享资源 在使用线程的时候,一个很重要的问题是要避免多个线程对同一变量或其它资源的访问冲突.一旦你稍不留神,重叠访问.在多个线程中修改(共享资源)等这些操作会导致各种各样的问题:更严重的是,这些问题一般只会在比较极端(比如高并发.生产服务器.甚至在性能更好的硬件设备上)的情况下才会出现. 比如有这样一个情况:需要追踪对一事件处理的次数 counter = 0 def process_item(item): global counter ... do something with item

使用Python中的线程进行网络编程的入门教程

引言 对于 Python 来说,并不缺少并发选项,其标准库中包括了对线程.进程和异步 I/O 的支持.在许多情况下,通过创建诸如异步.线程和子进程之类的高层模块,Python 简化了各种并发方法的使用.除了标准库之外,还有一些第三方的解决方案,例如 Twisted.Stackless 和进程模块.本文重点关注于使用 Python 的线程,并使用了一些实际的示例进行说明.虽然有许多很好的联机资源详细说明了线程 API,但本文尝试提供一些实际的示例,以说明一些常见的线程使用模式. 全局解释器锁 (G

详解python中的线程

Python中创建线程有两种方式:函数或者用类来创建线程对象. 函数式:调用 _thread 模块中的start_new_thread()函数来产生新线程. 类:创建threading.Thread的子类来包装一个线程对象. 1.线程的创建 1.1 通过thread类直接创建 import threading import time def foo(n): time.sleep(n) print("foo func:",n) def bar(n): time.sleep(n) prin

解决Python中定时任务线程无法自动退出的问题

python的线程有一个类叫Timer可以,用来创建定时任务,但是它的问题是只能运行一次,如果要重复执行,则只能在任务中再调用一次timer,但这样就存在新的问题了,就是在主进程退出后,不能正常退出子线程. from threading import Timer def scheduletaskwrap(): pritn "in task" Timer(10, scheduletaskwrap).start() Timer(10, scheduletaskwrap).start() 象

详解python中的线程与线程池

线程 进程和线程 什么是进程? 进程就是正在运行的程序, 一个任务就是一个进程, 进程的主要工作是管理资源, 而不是实现功能 什么是线程? 线程的主要工作是去实现功能, 比如执行计算. 线程和进程的关系就像员工与老板的关系, 老板(进程) 提供资源 和 工作空间, 员工(线程) 负责去完成相应的任务 特点 一个进程至少由一个线程, 这一个必须存在的线程被称为主线程, 同时一个进程也可以有多个线程, 即多线程 当我们我们遇到一些需要重复执行的代码时, 就可以使用多线程分担一些任务, 进而加快运行速

解析python 中/ 和 % 和 //(地板除)

python / 和 % 和 //(地板除)用于对数据进行除法运算. python中 // 和 / 和 % 简介 python中与除法相关的三个运算符是// 和 / 和 %,下面逐一介绍. "/",这是传统的除法,3/2=1.5 "//",在python中,这个叫"地板除",3//2=1 "%",这个是取模操作,也就是区余数,4%2=0,5%2=1 Python中分为3种除法:1./,2.%,3.//. 1./ 基于 pyth

详解C语言和Python中的线程混用

问题 你有一个程序需要混合使用C.Python和线程, 有些线程是在C中创建的,超出了Python解释器的控制范围. 并且一些线程还使用了Python C API中的函数. 解决方案 如果你想将C.Python和线程混合在一起,你需要确保正确的初始化和管理Python的全局解释器锁(GIL). 要想这样做,可以将下列代码放到你的C代码中并确保它在任何线程被创建之前被调用. #include <Python.h> ... if (!PyEval_ThreadsInitialized()) { P

深入解析Python中函数的参数与作用域

传递参数 函数传递参数时的一些简要的关键点: 参数的传递是通过自动将对象赋值给本地变量名来实现的.所有的参数实际上都是通过指针进行传递的,作为参数被传递的对象从来不自动拷贝. 在函数内部的参数名的赋值不会影响调用者. 改变函数的可变对象参数的值会对调用者有影响. 实际上,Python的参数传递模型和C语言的相当相似: 不可变参数"通过值"进行传递.像整数和字符串这样的对象是通过对象引用而不是拷贝进行的,但是因为不论怎么样都不可能在原处改变不可变对象,实际的效果就很像创建了一份拷贝. 可

深入解析Python中的lambda表达式的用法

普通的数学运算用这个纯抽象的符号演算来定义,计算结果只能在脑子里存在.所以写了点代码,来验证文章中介绍的演算规则. 我们来验证文章里介绍的自然数及自然数运算规则.说到自然数,今天还百度了一下,据度娘说,1993年后国家规定0是属于自然数.先定义自然数及自然数的运算规则: 用lambda表达式定义自然数(邱齐数) 0 := λf.λx.x 1 := λf.λx.f x 2 := λf.λx.f (f x) 3 := λf.λx.f (f (f x)) ... 上面定义直观的意思就是数字n, 是f(

解析Python中的变量、引用、拷贝和作用域的问题

在Python中,变量是没有类型的,这和以往看到的大部分编辑语言都不一样.在使用变量的时候,不需要提前声明,只需要给这个变量赋值即可.但是,当用变量的时候,必须要给这个变量赋值:如果只写一个变量,而没有赋值,那么Python认为这个变量没有定义.如下: >>> a Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'a'