python同步线程

news2024/11/18 15:27:55

线程同步可以定义为一种方法,借助这种方法,可以确信两个或更多的并发线程不会同时访问被称为临界区的程序段。 另一方面,正如我们所知道的那样,临界区是共享资源被访问的程序的一部分。 因此,同步是通过同时访问资源来确保两个或更多线程不相互连接的过程。 下图显示了四个线程同时尝试访问程序的临界区。

在这里插入图片描述
为了使它更清楚,假设有两个或更多线程试图同时在列表中添加对象。 这种行为不能导致成功的结局,因为它会抛弃一个或所有的对象,或者它会完全破坏列表的状态。 这里同步的作用是每次只有一个线程可以访问列表

线程同步的问题

在实现并发编程或应用同步基元时,可能会遇到问题。 在本节中,我们将讨论两个主要问题。 问题是 -

  • 死锁
  • 竞争条件

1. 竞争条件

这是并发编程的主要问题之一。 对共享资源的并发访问可能会导致竞争状态。 竞争条件可以定义为当两个或更多线程可以访问共享数据,然后尝试同时更改其值时发生的条件。 由此,变量的值可能是不可预知的,并且取决于进程的上下文切换的时间而变化
示例
考虑这个例子来理解竞争条件的概念 -
第1步 - 在这一步中,需要导入线程模块 -

import threading

第2步 - 现在,定义一个全局变量,例如x,以及其值为0 -

x = 0

第3步 - 现在,需要定义increment_global()函数,该函数将在此全局函数中执行x递增1 -

def increment_global():

   global x
   x += 1

第4步 - 在这一步中,将定义taskofThread()函数,它将调用increment_global()函数达指定的次数; 在这个例子中,它是50000次 -

def taskofThread():

   for _ in range(50000):
      increment_global()

第5步 - 现在,定义创建线程t1和t2的main()函数。 两者都将在start()函数的帮助下启动,并等待join()函数完成作业。

def main():
   global x
   x = 0

   t1 = threading.Thread(target= taskofThread)
   t2 = threading.Thread(target= taskofThread)

   t1.start()
   t2.start()

   t1.join()
   t2.join()

第6步 - 现在,需要给出范围,如想要调用main()函数的迭代次数。 在这里,调用为5次。

if __name__ == "__main__":
   for i in range(5):
      main()
      print("x = {1} after Iteration {0}".format(i,x))

在下面显示的输出中,我们可以看到竞争条件的影响,因为在每次迭代之后x的值预计为100000。但是,值有很大的变化。 这是由于线程对共享全局变量x的并发访问

x = 100000 after Iteration 0
x = 54034 after Iteration 1
x = 80230 after Iteration 2
x = 93602 after Iteration 3
x = 93289 after Iteration 4

处理使用锁定的竞争条件

正如我们已经看到上述程序中竞争条件的影响,我们需要一个同步工具,它可以处理多个线程之间的竞争条件。 在Python中,threading模块提供了Lock类来处理竞争条件。 此外,Lock类提供了不同的方法,可以通过它帮助处理多个线程之间的竞争条件。 方法如下所述 -
acquire()方法该方法用于获取,即阻止锁定。 锁可以是阻塞或非阻塞取决于以下真或假的值 -

  • 将值设置为True - 如果使用默认参数True调用acquire()方法,则线程执行将被阻止,直到解锁锁。
  • 将值设置为False - 如果acquire()方法使用False调用,而False不是默认参数,那么线程执行不会被阻塞,直到它被设置为true,即直到它被锁定

release()方法此方法用于释放锁。 以下是与此方法相关的一些重要任务 -

  • 如果锁定被锁定,那么release()方法将解锁它。 它的工作是如果多个线程被阻塞并且等待锁被解锁,则只允许一个线程继续。
  • 如果锁已经解锁,它将引发一个ThreadError错误。

现在,我们可以用锁类及其方法重写上述程序,以避免竞争条件。 我们需要使用lock参数定义taskofThread()方法,然后使用acquire()和release()方法来阻塞和非阻塞锁以避免竞争状况

示例
以下是用于理解处理竞争条件的锁概念的python程序示例 -

import threading

x = 0

def increment_global():

   global x
   x += 1

def taskofThread(lock):

   for _ in range(50000):
      lock.acquire()
      increment_global()
      lock.release()

def main():
   global x
   x = 0

   lock = threading.Lock()
   t1 = threading.Thread(target = taskofThread, args = (lock,))
   t2 = threading.Thread(target = taskofThread, args = (lock,))

   t1.start()
   t2.start()

   t1.join()
   t2.join()

if __name__ == "__main__":
   for i in range(5):
      main()
      print("x = {1} after Iteration {0}".format(i,x))

以下输出显示竞争条件的影响被忽略; 在每次迭代之后,x的值现在是100000,这与该程序的期望值相同。

x = 100000 after Iteration 0
x = 100000 after Iteration 1
x = 100000 after Iteration 2
x = 100000 after Iteration 3
x = 100000 after Iteration 4

僵局 - 餐饮哲学家的问题

死锁是设计并发系统时可能遇到的麻烦问题。可以在餐饮哲学家问题的帮助下说明这个问题,如下所示 -
Edsger Dijkstra最初介绍了餐饮哲学家问题,这是着名的并发系统最大问题和死锁问题之一。
在这个问题中,有五位着名的哲学家坐在圆桌旁,从碗里吃着一些食物。 五种哲学家可以使用五种叉子来吃他们的食物。 然而,哲学家决定同时使用两把叉子来吃他们的食物。
现在,哲学家有两个主要条件。 首先,每个哲学家既可以进食也可以处于思维状态,其次,他们必须首先获得叉子,即左边和右边的叉子。 当五位哲学家中的每一位设法同时选择左叉时,问题就出现了。他们都在等待右叉是自由的,但他们在未吃完了食物之前永远不会放弃他们的叉子,并且永远不会有右叉。 因此,餐桌上会出现僵局。

并发系统中的死锁
现在如果我们看到,并发系统也会出现同样的问题。 上面例子中的叉子是系统资源,每个哲学家都可以表示这个竞争获取资源的过程。
Python程序的解决方案
通过将哲学家分为两种类型 - 贪婪的哲学家和慷慨的哲学家,可以找到解决这个问题的方法。 主要是一个贪婪的哲学家会尝试拿起左边的叉子,等到左边的叉出现。 然后,他会等待右叉子在那里,拿起来,吃,然后把它放下。 另一方面,一个慷慨的哲学家会尝试拿起左边的叉子,如果它不在那里,他会等一段时间再试一次。 如果他们拿到左边的叉子,他们会尝试找到右叉子。 如果他们也会得到正确的叉子,那么他们会吃饭并释放叉子。 但是,如果他们不能得到右叉子,那么他们也会释放左叉子。
示例
以下Python程序将帮助找到解决哲学家就餐问题的方案

import threading
import random
import time

class DiningPhilosopher(threading.Thread):

   running = True

   def __init__(self, xname, Leftfork, Rightfork):
       threading.Thread.__init__(self)
       self.name = xname
       self.Leftfork = Leftfork
       self.Rightfork = Rightfork

   def run(self):
       while(self.running):
          time.sleep( random.uniform(3,13))
          print ('%s is hungry.' % self.name)
          self.dine()

   def dine(self):
       fork1, fork2 = self.Leftfork, self.Rightfork

       while self.running:
          fork1.acquire(True)
          locked = fork2.acquire(False)
          if locked: break
              fork1.release()
              print ('%s swaps forks' % self.name)
              fork1, fork2 = fork2, fork1
           else:
              return

       self.dining()
       fork2.release()
       fork1.release()

   def dining(self):
       print ('%s starts eating '% self.name)
       time.sleep(random.uniform(1,10))
       print ('%s finishes eating and now thinking.' % self.name)

def Dining_Philosophers():
   forks = [threading.Lock() for n in range(5)]
   philosopherNames = ('1st','2nd','3rd','4th', '5th')

   philosophers= [DiningPhilosopher(philosopherNames[i], forks[i%5], forks[(i+1)%5]) \
      for i in range(5)]

   random.seed()
   DiningPhilosopher.running = True
   for p in philosophers: p.start()
   time.sleep(30)
   DiningPhilosopher.running = False
   print (" It is finishing.")

Dining_Philosophers()

上面的程序使用了贪婪和慷慨的哲学家的概念。 该程序还使用了threading模块的Lock类的acquire()和release()方法。 我们可以在下面的输出中看到解决方案 -

4th is hungry.
4th starts eating
1st is hungry.
1st starts eating
2nd is hungry.
5th is hungry.
3rd is hungry.
1st finishes eating and now thinking.3rd swaps forks
2nd starts eating
4th finishes eating and now thinking.
3rd swaps forks5th starts eating
5th finishes eating and now thinking.
4th is hungry.
4th starts eating
2nd finishes eating and now thinking.
3rd swaps forks
1st is hungry.
1st starts eating
4th finishes eating and now thinking.
3rd starts eating
5th is hungry.
5th swaps forks
1st finishes eating and now thinking.
5th starts eating
2nd is hungry.
2nd swaps forks
4th is hungry.
5th finishes eating and now thinking.
3rd finishes eating and now thinking.
2nd starts eating 4th starts eating
It is finishing.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/374880.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MIT 6.S081学习笔记

计划花25天时间学完6.S081课程,从2月20日-3月20日。课程主页Link   xv6 book   GDB User Manual Lecture 1: Introduction and Examples课程主题:设计和实现操作系统   OS的三大功能:多路复用、隔离和交互。 Lab: Xv6 and Unix utiliti…

“ChatGPT之父”Sam Altman:我是如何成功的?

背靠微软,OpenAI能拳打谷歌,脚踢Meta,它背后的男人,必然不简单。 让我们来看一看,Sam Altman是如何一步步成长为今天这个搅动全世界的男人。 山姆奥特曼(Sam Altman) 成长和创业经历 在YC创始…

数据结构(Java版)绪论

一、数据结构绪论 1、概论 🍎数据结构研究计算机的操作对象以及他们之间的关系和操作。 2、算法的定义、特征、设计要求 算法:是对特定问题求解步骤的一种描述,它是指令的有限序列,是一系列输入转化为输出的计算步骤。 算法的特…

篮球杯 双指针专题

总的来说&#xff0c;双指针分为while(1)类型和尺取法类型可以解决各种问题&#xff08;如子序列问题&#xff09;活动 - AcWing思路&#xff1a;while(1)型的双指针基本形式为&#xff1a;while(1){if(l>n||r>n) break;while(条件&&l<n) l;rl;while(条件&…

使用Platform Designer创建Nios II 最小系统

Nios II简介 ​ Nios II 软核处理器十多年前就有了&#xff0c;它和xilinx的MicroBlaze类似&#xff0c;性能相比硬核处理器要差得多&#xff0c;工程应用也不是很多&#xff0c;那还有必须学习一下吗&#xff1f;我个人认为了解一下Nios II开发流程&#xff0c;对intel FPGA开…

9.网站数据统计

1.Redis 高级数据类型&#xff08;1&#xff09;HyperLogLog统计20万个重复数据的独立总数// 统计20万个重复数据的独立总数. Test public void testHyperLogLog() {String redisKey "test:hll:01";for (int i 1; i < 100000; i) {redisTemplate.opsForHyperLog…

8年测开经验面试28K公司后,吐血整理出高频面试题和答案

#01、如何制定测试计划&#xff1f; ❶参考点 1.是否拥有测试计划的制定经验 2.是否具备合理安排测试的能力 3.是否具备文档输出的能力 ❷面试命中率 80% ❸参考答案 测试计划包括测试目标、测试范围、测试环境的说明、测试类型的说明&#xff08;功能&#xff0c;安全&am…

深入解读.NET MAUI音乐播放器项目(三):界面交互

UI设计的本质是对于产品的理解在界面中多种形式的映射&#xff0c;当需求和定位不同时&#xff0c;对相同的功能表达出了不同的界面和交互方式。 作为播放器&#xff0c;界面可以是千差万别的。《番茄播放器》的iOS平台上我开发了传统版本&#xff0c;和基于手势播放的版本。 …

Word处理控件Aspose.Words功能演示:使用 C++ 在 Word (DOC/DOCX) 中添加或删除水印

Aspose.Words 是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。此外&#xff0c; Aspose API支持流行文件格式处…

Nacos未授权访问漏洞

Nacos介绍 Nacos 的官网地址为&#xff1a; https://nacos.io 它是阿里开源的 SpringCloud Alibaba 项目下的一项技术&#xff0c;可以实现服务注册中心、分布式配置中心。 一般来说&#xff0c;nacos被建议部署在内网中&#xff0c;如果在外网出现&#xff0c;会有很大的风险…

NCRE计算机等级考试Python真题(三)

第三套试题1、按照“后进先出”原则组织数据的数据结构是_______A.栈B.双向链表C.二叉树D.队列正确答案&#xff1a; A2、以下选项的叙述中&#xff0c;正确的是&#xff1a;A.在循环队列中&#xff0c;只需要队头指针就能反映队列中元素的动态变化情况B.在循环队列中&#xff…

数学小课堂:虚数的媒介工具作用(虚构一个现实中不存在的概念,来解决现实问题)

文章目录 引言I 预备知识1.1 平方根1.2 三次方程1.3 极坐标II 虚数2.1 虚数的来源2.2 理解虚数存在的必要性2.3 虚数的影响III 复数3.1 人类认知升级的过程3.2 数字的扩展历史3.3 复数的用途引言 虚数的来源和存在的必要性:三次方程是一定有实数解的,因此根号里面负数的问题…

SREWorks前端低代码组件生态演进:monorepo架构重构和远程组件加载实践

作者&#xff1a;王威&#xff08;地谦&#xff09; 文章结构 项目背景演进分析monorepo架构演进 Webpack与Rollup如何平滑迁移构建优化 组件的可扩展与可插拔演进总结版本动态 项目背景 SREWorks是一个面向企业级复杂业务的开源云原生数智运维平台&#xff0c;是大数据SR…

wafw00f 防火墙探测

kali机器自带防火墙探测工具wafw00&#xff0c;它可以通过发送正常以及不正常甚至包含恶意代码的HTTP请求&#xff0c;来探测网站是否存在防火墙&#xff0c;并识别防火墙的厂商及类型。安装&#xff1a;git clone https://github.com/EnableSecurity/wafw00f.git python setup…

Windows下载安装Prometheus

目录 资料 下载 解压 点击prometheus.exe运行 资料 Prometheus是一个开源的系统监控和报警系统&#xff0c;同时也支持多种exporter采集数据&#xff0c;还支持pushgateway进行数据上报&#xff0c;Prometheus性能足够支撑上万台规模的集群。 官网&#xff1a;https://pr…

DSIN模型

DSIN模型提出得动机&#xff1a;用户得行为是由会话组成得&#xff0c;在每个会话内部用户得行为是相似得&#xff0c;会话之间得用户的行为是存在较大差异性得&#xff0c;而其他模型都没有关注这点&#xff0c;所以就有了DSIN模型。 在这里我们来讲下DSIN关键得四层&#xf…

PR9268/300-000库存现货振动传感器 雄霸工控

PR9268/300-000库存现货振动传感器 雄霸工控PR9268/300-000库存现货振动传感器 雄霸工控SDM010PR9670/110-100PR9670/010-100PR9670/003-000PR9670/002-000PR9670/001-000PR9670/000-000PR9600/014-000PR9600/011-000PR9376/010-021PR9376/010-011PR9376/010-011PR9376/010-001…

2023年最新qq空间说说怎么全部删除_QQ空间说说如何批量删除

2023年最新QQ空间自动删除说说_2023批量删除QQ空间说说插件小工具_QQ空间如何一次性批量删除说说 一千多条说说怎么删&#xff1f;QQ空间说怎么批量删除_怎样把发的空间说说全删了 使用谷歌浏览器插件&#xff0c;一键安装之后&#xff0c;就可以实现「自动删除」和「手动批量…

SQL入门DEMO

单表查询 ● --查询订购日期在1996年7月1日至1996年7月15日之间的订单的订购日期、订单ID、客户ID和雇员ID等字段的值 ● --查询供应商的ID、公司名称、地区、城市和电话字段的值。条件是“地区等于华北”并且“联系人头衔等于销售代表”。 –查询供应商的ID、公司名称、地…

如何将Google浏览器安装到D盘(内含教学视频)

如何将Google浏览器安装到D盘&#xff08;内含教学视频&#xff09; 教学视频下载链接地址&#xff1a;https://download.csdn.net/download/weixin_46411355/87503968 目录如何将Google浏览器安装到D盘&#xff08;内含教学视频&#xff09;教学视频下载链接地址&#xff1a;…