Python线程同步的实现代码

本文介绍Python中的线程同步对象,主要涉及 thread 和 threading 模块。

threading 模块提供的线程同步原语包括:Lock、RLock、Condition、Event、Semaphore等对象。

线程执行

join与setDaemon

子线程在主线程运行结束后,会继续执行完,如果给子线程设置为守护线程(setDaemon=True),主线程运行结束子线程即结束;

如果join()线程,那么主线程会等待子线程执行完再执行。

import threading
import time

def get_thread_a():
 print("get thread A started")
 time.sleep(3)
 print("get thread A end")

def get_thread_b():
 print("get thread B started")
 time.sleep(5)
 print("get thread B end")

if __name__ == "__main__":
 thread_a = threading.Thread(target=get_thread_a)
 thread_b = threading.Thread(target=get_thread_b)
 start_time = time.time()
 thread_b.setDaemon(True)
 thread_a.start()
 thread_b.start()
 thread_a.join()

 end_time = time.time()
 print("execution time: {}".format(end_time - start_time))

thread_a是join,首先子线程thread_a执行,thread_b是守护线程,当主线程执行完后,thread_b不会再执行执行结果如下:

get thread A started
get thread B started
get thread A end
execution time: 3.003199815750122

线程同步

当线程间共享全局变量,多个线程对该变量执行不同的操作时,该变量最终的结果可能是不确定的(每次线程执行后的结果不同),如:对count变量执行加减操作,count的值是不确定的,要想count的值是一个确定的需对线程执行的代码段加锁。

python对线程加锁主要有Lock和Rlock模块

Lock: 

from threading import Lock
lock = Lock()
lock.acquire()
lock.release()

Lock有acquire()和release()方法,这两个方法必须是成对出现的,acquire()后面必须release()后才能再acquire(),否则会造成死锁

Rlock:

鉴于Lock可能会造成死锁的情况,RLock(可重入锁)对Lock进行了改进,RLock可以在同一个线程里面连续调用多次acquire(),但必须再执行相同次数的release()

from threading import RLock
lock = RLock()
lock.acquire()
lock.acquire()
lock.release()
lock.release()

condition(条件变量),线程在执行时,当满足了特定的条件后,才可以访问相关的数据

import threading

def get_thread_a(condition):
 with condition:
  condition.wait()
  print("A : Hello B,that's ok")
  condition.notify()
  condition.wait()
  print("A : I'm fine,and you?")
  condition.notify()
  condition.wait()
  print("A : Nice to meet you")
  condition.notify()
  condition.wait()
  print("A : That's all for today")
  condition.notify()

def get_thread_b(condition):
 with condition:
  print("B : Hi A, Let's start the conversation")
  condition.notify()
  condition.wait()
  print("B : How are you")
  condition.notify()
  condition.wait()
  print("B : I'm fine too")
  condition.notify()
  condition.wait()
  print("B : Nice to meet you,too")
  condition.notify()
  condition.wait()
  print("B : Oh,goodbye")

if __name__ == "__main__":
 condition = threading.Condition()
 thread_a = threading.Thread(target=get_thread_a, args=(condition,))
 thread_b = threading.Thread(target=get_thread_b, args=(condition,))
 thread_a.start()
 thread_b.start() 

Condition内部有一把锁,默认是RLock,在调用wait()和notify()之前必须先调用acquire()获取这个锁,才能继续执行;当wait()和notify()执行完后,需调用release()释放这个锁,在执行with condition时,会先执行acquire(),with结束时,执行了release();所以condition有两层锁,最底层锁在调用wait()时会释放,同时会加一把锁到等待队列,等待notify()唤醒释放锁

wait() :允许等待某个条件变量的通知,notify()可唤醒

notify(): 唤醒等待队列wait()

执行结果:

B : Hi A, Let's start the conversation
A : Hello B,that's ok
B : How are you
A : I'm fine,and you?
B : I'm fine too
A : Nice to meet you
B : Nice to meet you,too
A : That's all for today
B : Oh,goodbye

Semaphore(信号量)

用于控制线程的并发数,如爬虫中请求次数过于频繁会被禁止ip,每次控制爬取网页的线程数量可在一定程度上防止ip被禁;文件读写中,控制写线程每次只有一个,读线程可多个。

import time
import threading

def get_thread_a(semaphore,i):
 time.sleep(1)
 print("get thread : {}".format(i))
 semaphore.release()

def get_thread_b(semaphore):
 for i in range(10):
  semaphore.acquire()
  thread_a = threading.Thread(target=get_thread_a, args=(semaphore,i))
  thread_a.start()

if __name__ == "__main__":
 semaphore = threading.Semaphore(2)
 thread_b = threading.Thread(target=get_thread_b, args=(semaphore,))
 thread_b.start()

上述示例了每隔1秒并发两个线程执行的情况,当调用一次semaphore.acquire()时,Semaphore的数量就减1,直至Semaphore数量为0时被锁上,当release()后Semaphore数量加1。Semaphore在本质上是调用的Condition,semaphore.acquire()在Semaphore的值为0的条件下会调用Condition.wait(), 否则将值减1,semaphore.release()会将Semaphore的值加1,并调用Condition.notify()

Semaphore源码

def acquire(self, blocking=True, timeout=None):
  if not blocking and timeout is not None:
   raise ValueError("can't specify timeout for non-blocking acquire")
  rc = False
  endtime = None
  with self._cond:
   while self._value == 0:
    if not blocking:
     break
    if timeout is not None:
     if endtime is None:
      endtime = _time() + timeout
     else:
      timeout = endtime - _time()
      if timeout <= 0:
       break
    self._cond.wait(timeout)
   else:
    self._value -= 1
    rc = True
  return rc

def release(self):
  with self._cond:
   self._value += 1
   self._cond.notify()

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • Python中的函数式编程:不可变的数据结构

    让我们首先考虑正方形和长方形.如果我们认为在接口方面,忽略了实现细节,方块是否是矩形的子类型? 子类型的定义取决于Liskov代换原理.为了成为一个子类型,它必须能够完成超级类型所做的一切. 如何定义矩形的接口? zope.interface import Interface class IRectangleInterface: get_length: """Squares can do that""" get_width: "&quo

  • 10个Python小技巧你值得拥有

    列表推导式 你有一个list: bag = [1, 2, 3, 4, 5] 现在你想让所有元素翻倍,让它看起来是这个样子: [2, 4, 6, 8, 10] 大多初学者,根据之前语言的经验会大概这样来做 bag = [1, 2, 3, 4, 5] for i in range(len(bag)): bag[i] = bag[i] * 2 但是有更好的方法: bag = [elem * 2 for elem in bag] 很简洁对不对?这叫做Python的列表推导式 . 遍历列表 还是上面的列表

  • Python实现App自动签到领取积分功能

    要自动签到,最简单的是打开页面分析请求,然后我们用脚本实现请求的自动化.但是发现食行没有页面,只有 APP,这不是一个好消息,这意味着需要抓包处理了. 下面的操作就好办了,在电脑端的浏览器打开网址,按下 F12,开始起飞~ 登录分析 点击签到后,会跳转到用户登录页面: https://wechatx.34580.com/mart/#/sign/in,输入登录信息后,点击登录,同时关注开发调试栏的网络交互信息 . 可以发现,登录的请求地址是: https://wechatx.34580.com/s

  • Python中GIL的使用详解

    1.GIL简介 GIL的全称为Global Interpreter Lock,全局解释器锁. 1.1 GIL设计理念与限制 python的代码执行由python虚拟机(也叫解释器主循环,CPython版本)来控制,python在设计之初就考虑到在解释器的主循环中,同时只有一个线程在运行.即在任意时刻只有一个线程在解释器中运行.对python虚拟机访问的控制由全局解释锁GIL控制,正是这个锁来控制同一时刻只有一个线程能够运行. 在调用外部代码(如C.C++扩展函数)的时候,GIL将会被锁定,直到这

  • 面向初学者的Python编辑器Mu

    Meet Mu,一个开放源码编辑器,使学生们更容易学习编写Python代码. Mu一个开源编辑器,是满足学生可以轻松学习编写Python代码的工具.作为初学程序员的Python编辑器,旨在使学习体验更加愉快.它使学生​​能够在早期体验Python的编程,而这对于初学者学习新知识的很重要. 如果你曾试图教年轻人如何编程,你会立即掌握Mu的重要性.大多数编程工具都是由开发人员为开发人员编写的,并不适合初学程序员,无论他们的年龄如何.然而,Mu是由老师为学生而设计的工具. Mu是由Nicholas T

  • 使用EduBlock轻松学习Python编程

    如果你正在寻找一种方法将你的学生(或你自己)从使用 Scratch 编程转移到学习 Python,我建议你了解一下 EduBlocks.它为 Python 3 编程带来了熟悉的拖放式图形用户界面(GUI). 从 Scratch 过渡到 Python 的一个障碍是缺少拖放式 GUI,而正是这种拖放式 GUI 使得 Scratch 成为 K-12 学校的应用程序.EduBlocks 的拖放版的 Python 3 改变了这种范式.它的目的是"帮助教师在较早的时候向儿童介绍基于文本的编程语言,如 Pyt

  • 3个用于数据科学的顶级Python库

    Python有许多吸引力,如效率,代码可读性和速度,使其成为数据科学爱好者的首选编程语言.Python通常是希望升级其应用程序功能的数据科学家和机器学习专家的首选. 由于其广泛的用途,Python拥有大量的库,使数据科学家可以更轻松地完成复杂的任务,而无需很多编写代码的麻烦.以下是数据科学的前3个Python库. 使用这些库将Python转化为一个科学的数据分析和建模工具. 1.NumPy NumPy(Numerical Python的缩写)是配备有用资源的顶级库之一,可帮助数据科学家将Pyth

  • Python 使用类写装饰器的小技巧

    最近学到了一个有趣的装饰器写法,就记录一下. 装饰器是一个返回函数的函数.写一个装饰器,除了最常见的在函数中定义函数以外,Python还允许使用类来定义一个装饰器. 1.用类写装饰器 下面用常见的写法实现了一个缓存装饰器. def cache(func): data = {} def wrapper(*args, **kwargs): key = f'{func.__name__}-{str(args)}-{str(kwargs)})' if key in data: result = data

  • 一行代码让 Python 的运行速度提高100倍

    python一直被病垢运行速度太慢,但是实际上python的执行效率并不慢,慢的是python用的解释器Cpython运行效率太差. "一行代码让python的运行速度提高100倍"这绝不是哗众取宠的论调. 我们来看一下这个最简单的例子,从1一直累加到1亿. 最原始的代码: import time def foo(x,y): tt = time.time() s = 0 for i in range(x,y): s += i print('Time used: {} sec'.form

  • Python线程同步的实现代码

    本文介绍Python中的线程同步对象,主要涉及 thread 和 threading 模块. threading 模块提供的线程同步原语包括:Lock.RLock.Condition.Event.Semaphore等对象. 线程执行 join与setDaemon 子线程在主线程运行结束后,会继续执行完,如果给子线程设置为守护线程(setDaemon=True),主线程运行结束子线程即结束: 如果join()线程,那么主线程会等待子线程执行完再执行. import threading import

  • Python多进程同步简单实现代码

    本文讲述了Python多进程同步简单实现代码.分享给大家供大家参考,具体如下: #encoding=utf8 from multiprocessing import Process, Lock def func(lock, a): lock.acquire() print a lock.release() if __name__ == '__main__': lock = Lock() workers = [] # 创建两个进程 for i in range(0, 2): p = Process

  • python批量同步web服务器代码核心程序

    #!/usr/bin/env python #coding:utf8 import os,sys import md5,tab from mysql_co.my_db import set_mysql from ssh_co.ssh_connect import sshd from ssh_co.cfg.config import ssh_message,item_path from file import findfile def my_mysql(): db_file={} my_conne

  • 浅谈Python线程的同步互斥与死锁

    线程间通信方法 1. 通信方法 线程间使用全局变量进行通信     2. 共享资源争夺 共享资源:多个进程或者线程都可以操作的资源称为共享资源.对共享资源的操作代码段称为临界区. 影响 : 对共享资源的无序操作可能会带来数据的混乱,或者操作错误.此时往往需要同步互斥机制协调操作顺序.     3. 同步互斥机制 同步 : 同步是一种协作关系,为完成操作,多进程或者线程间形成一种协调,按照必要的步骤有序执行操作.两个或两个以上的进程或线程在运行过程中协同步调,按预定的先后次序运行.比如 A 任务的

  • Python线程之同步机制实际应用场景举例说明

    目录 一.举例银行转账 二.问题解决 三.总结 这次让我们来看看一个真实场景吧:银行转账 一.举例银行转账 假设现在有一个xuewei的账号里面有 100W. 然后有多个任务在转账,转入转出都是跟这个xuewei账号相关的. 而且这些任务发生是随机的. 我们先把上面的场景写成代码: xuewei_account = 100 # amount为负数即是转出金额 def transfer(money):     global xuewei_account     xuewei_account +=

  • 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线程中同步锁详解

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

  • Python的线程之线程同步

    目录 线程同步 threading.Lock获取同步锁 总结 在多线程程序中,它们互相独立打印的时间却是错乱的! 如下图,明明t-0 > t-1 > t-2 (按照线程创建时间早晚排列).最后输出居然是t-1最落后. 我们怎么样做避免错乱呢, 下面看看. 线程同步 多线程,就是多个独立的运行单位,同时执行同样的事情. 多线程不是已经做到同时执行了吗?还需要同步干嘛? 是的,线程是同时被调用执行了,但是每个线程之间互相独立,也互相竞争了. 这就跟跑道上有3个运动员,枪响之后同时开跑,但是他们通常

  • Python线程池thread pool创建使用及实例代码分享

    目录 前言 一.线程 1.线程介绍 2.线程特性 轻型实体 独立调度和分派的基本单位 可并发执行 4)共享进程资源 二.线程池 三.线程池的设计思路 四.Python线程池构建 1.构建思路 2.实现库功能函数 ThreadPoolExecutor() submit() result() cancel() cancelled() running() as_completed() map() 前言 首先线程和线程池不管在哪个语言里面,理论都是通用的.对于开发来说,解决高并发问题离不开对多个线程处理

  • Python并行编程多线程锁机制Lock与RLock实现线程同步

    目录 什么是锁机制? Lock() 管理线程 RLock() 与Lock()的区别 什么是锁机制? 要回答这个问题,我们需要知道为什么需要使用锁机制.前面我们谈到一个进程内的多个线程的某些资源是共享的,这也是线程的一大优势,但是也随之带来一个问题,即当两个及两个以上的线程同时访问共享资源时,如果此时没有预设对应的同步机制,就可能带来同一时刻多个线程同时访问同一个共享资源,即出现竞态,多数情况下我们是不希望出现这样的情况的,那么怎么避免呢? Lock() 管理线程 先看一段代码: import t

随机推荐