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

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

线程同步的问题

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

死锁

竞争条件

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.

¥ 我要打赏

纠错/补充

收藏

加QQ群啦,易百教程官方技术学习群

注意:建议每个人选自己的技术方向加群,同一个QQ最多限加 3 个群。

python多线程并发编程技术_同步线程 - Python并发编程教程™相关推荐

  1. python多线程并发编程技术_三 python并发编程之多线程-理论

    一 什么是线程 在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程 线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程 车间负责把资源整合 ...

  2. 计算机编程导论python程序设计答案-学堂在线_计算机科学与Python编程导论_作业课后答案...

    学堂在线_计算机科学与Python编程导论_作业课后答案 答案: 更多相关问题 近代中国完全沦为半殖民地半封建社会的标志是:A.<马关条约>B.<辛丑条约>C.<凡尔赛和 ...

  3. python如何初始化对象数组_如何理解Python中的面向对象编程?

    (由Python大本营付费下载自视觉中国) 作者 | Radek Fabisiak 译者 | 弯月,责编 | 郭芮 出品 | CSDN(ID:CSDNnews) 现如今面向对象编程的使用非常广泛,本文 ...

  4. python都有什么包_常用的Python包有哪些,如何快速掌握Python编程

    Python入门简单.功能强大,是很多想要入行IT开发行业人士的主要选择.Python包可以帮助Web开发人员快速和容易地创建应用程序和功能,因此很多企业在招聘时都会考察求职者对于Python包的掌握 ...

  5. python windows窗口置顶_想用Python编程却不知如何下手?一篇搞定编程准备工作

    导读:为了简化Python编程的学习难度,开发过程中的工具.环境尽量使用同一套,此后所有的编程环境都以Windows系统下Python3.8为准,代码编辑器及IDE(集成开发环境)使用VS Code. ...

  6. python越学越糊涂_你越努力,编程水平越差!这样学 Python ,更容易成为高手!...

    原标题:你越努力,编程水平越差!这样学 Python ,更容易成为高手! 从事 Python 这些年中,我见过很多 Python 教程和书籍,他们大都这样讲 : 先介绍 Python 的基本语法规则. ...

  7. 编写python程序、计算账户余额_《易学Python》——第1章 为何学习Python 1.1 学习编程...

    本节书摘来自异步社区<易学Python>一书中的第1章,第1.1节,作者[澳]Anthony Briggs,王威,袁国忠 译,更多章节内容可以访问云栖社区"异步社区"公 ...

  8. 简述python程序的书写规范_简明的 Python 编程规范

    简明的 Python 编程规范 使用编码规范编写程序,不仅可以别人让后面维护的人更方便,同时也方便自己检查程序.以下是百分网小编精心为大家整理的简明的 Python 编程规范,希望对大家编写程序有所帮 ...

  9. 计算机编程导论python程序设计答案-学堂云_计算机科学与Python编程导论_作业课后答案...

    学堂云_计算机科学与Python编程导论_作业课后答案 答案: 更多相关问题 保本基金参与股指期货交易,应当根据风险管理的原则,以套期保值为目的.() 基金经理主要依据股票投资价值报告来决定实际的投资 ...

最新文章

  1. [QA]Python字节码优化问题
  2. 如何删除sublime目录
  3. 【原创】大数据基础之Spark(9)spark部署方式yarn/mesos
  4. Android多媒体开发:录音机
  5. windows连接远程Hadoop/Spark
  6. php 浮点型float 强转int php金额计算 php元转分
  7. JS 常用字符串数组遍历函数方法整理
  8. c51单片机led奇数偶数亮_两STM32单片机串口通讯实验
  9. [2021.1.27多校省选模拟10]跑步(线段树合并)
  10. Java和线性代数的关系_高等数学,线性代数与计算机的关系?
  11. 【Android图像处理】图像处理之-素描效果
  12. python伪装浏览器https_Python3 伪装浏览器的方法示例
  13. 传富士康将在印度建世界最大代工厂
  14. Oracle--sqlplus如何设置SQLPlus结果显示的宽度,ORACLE sqlplus提示符设置
  15. 【数据分析】基于matlab焊缝边缘检测算法对比分析 【含Matlab源码 260期】
  16. 再战图形,一图一世界
  17. Python|十五个超级炫酷的代码
  18. 写刀路的一些经验[分享] 铜公加工方法及注意事项
  19. HTML重构:工具篇
  20. Redis缓存(三)缓存异常的四个方面:数据同步、缓存雪崩、击穿、穿透

热门文章

  1. NLP十大数据扩充策略
  2. 码神日志N0.1|专场邀请:深度解析音视频技术(内有福利哦~)
  3. 如何创建高质量的TypeScript声明文件(五) - 示例
  4. docker环境搭建
  5. Centos 6.9中PHPmyadmin 的搭建,WordPress的搭建,Discuz的搭建
  6. 报名 | 想在硅谷近距离接触蚂蚁金服的CTO和一众技术高管?这个机会一定不能错过!...
  7. Sandcastle是微软提供的一个根据XML注释和DLL文件生成帮助文件的工具
  8. IIS7.0 部署wcf 404或者配置MIME(转)
  9. Chapter 1 Securing Your Server and Network(1):选择SQL Server运行账号
  10. 话里话外:实现信息化综合集成需要经历的五个阶段