Python多线程

Python线程同步

Python线程同步详细操作教程
可以将线程同步定义为一种方法,借助该方法,我们可以确保两个或多个并发线程不会同时访问被称为关键节的程序段。另一方面,我们知道关键部分是程序中访问共享资源的部分。因此,我们可以说同步是通过同时访问资源来确保两个或更多线程不相互接口的过程。下图显示了四个线程试图同时访问程序的关键部分。
线程同步
为使内容更清楚,假设有两个或更多线程试图同时在列表中添加对象。此操作无法导致成功结束,因为它会丢弃一个或所有对象,或者将完全破坏列表的状态。在这里,同步的作用是一次只能有一个线程访问该列表。

问题出现在线程同步中

在实现并发编程或应用同步原语时,我们可能会遇到问题。在本节中,我们将讨论两个主要问题。问题是-
僵局 种族状况

种族状况

这是并发编程中的主要问题之一。并发访问共享资源可能导致争用情况。竞争条件可以定义为当两个或多个线程可以访问共享数据然后尝试同时更改其值时发生的条件。因此,变量的值可能无法预测,并且会根据进程上下文切换的时间而变化。

示例

请考虑以下示例以了解竞争条件的概念-
步骤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 -现在,定义main()函数,在其中创建线程t1和t2。两者都将在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中, 模块提供了Lock类来处理竞争条件。此外, Lock 类提供了不同的方法,借助这些方法,我们可以处理多个线程之间的竞争条件。方法如下所述-

acquire()方法

此方法用于获取(即阻止锁)。锁可以是阻塞的也可以是非阻塞的,具体取决于以下true或false值-
值设置为True -如果使用True(默认参数)调用acquire()方法,则线程执行将被阻塞,直到锁被解锁。 将值设置为False -如果使用False(不是默认参数)调用acquire()方法,则在将其设置为true之前,不会阻塞线程执行。直到被锁定。

release()方法

此方法用于释放锁。以下是与此方法有关的一些重要任务-
如果锁被锁定,则 release()方法将其解锁。它的工作是,如果一个以上的线程被阻塞并等待锁被解锁,则只允许一个线程继续运行。 如果锁定已解锁,它将引发 ThreadError
现在,我们可以使用lock类及其方法重写上述程序,以避免出现竞争情况。我们需要先定义带有lock参数的taskofThread()方法,然后再使用acquire()和release()方法对锁进行阻塞和非阻塞以避免竞争状态。

示例

以下是python程序的示例,用于了解用于处理竞争条件的锁的概念-
# Filename : example.py
# Copyright : 2020 By Lidihuo
# Author by : www.lidihuo.com
# Date : 2020-08-22
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,这符合该程序的预期。

输出

# Filename : example.py
# Copyright : 2020 By Lidihuo
# Author by : www.lidihuo.com
# Date : 2020-08-22
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程序将帮助我们找到餐饮哲学家问题的解决方案-
# Filename : example.py
# Copyright : 2020 By Lidihuo
# Author by : www.lidihuo.com
# Date : 2020-08-22
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()
以上程序使用贪婪和慷慨的哲学家的概念。该程序还使用了 Lock 类的 acquire() release()方法>模块。我们可以在下面的输出中看到解决方案-

输出

# Filename : example.py
# Copyright : 2020 By Lidihuo
# Author by : www.lidihuo.com
# Date : 2020-08-22
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.
昵称: 邮箱:
Copyright © 2022 立地货 All Rights Reserved.
备案号:京ICP备14037608号-4