Python多线程

Python线程通信

Python线程通信详细操作教程
在现实生活中,如果一群人在共同完成一项任务,那么他们之间应该进行沟通以正确完成任务。同样的类比也适用于线程。在编程中,为了减少处理器的理想时间,我们创建了多个线程,并为每个线程分配了不同的子任务。因此,必须有一个通讯工具,并且他们应该彼此交互以同步方式完成工作。
请考虑以下与线程互通有关的要点-
没有性能提升-如果我们无法在线程和进程之间实现正确的通信,那么并发和并行性带来的性能提升就没有用了。 正确完成任务-线程之间没有适当的互通机制,分配的任务将无法正确完成。 比进程间通信更有效-线程间通信比进程间通信更高效,更易于使用,因为进程内的所有线程共享相同的地址空间,而不必使用共享的记忆。

用于线程安全通信的Python数据结构

多线程代码存在将信息从一个线程传递到另一个线程的问题。标准的通信原语不能解决此问题。因此,我们需要实现自己的复合对象,以便在线程之间共享对象,以使通信线程安全。以下是一些数据结构,在对它们进行了一些更改之后,它们提供了线程安全的通信-

集合

为了以线程安全的方式使用set数据结构,我们需要扩展set类以实现我们自己的锁定机制。

示例

这是扩展类的Python示例-
# Filename : example.py
# Copyright : 2020 By Lidihuo
# Author by : www.lidihuo.com
# Date : 2020-08-22
class extend_class(set):
   def __init__(self, *args, **kwargs):
      self._lock = Lock()
      super(extend_class, self).__init__(*args, **kwargs)
   def add(self, elem):
      self._lock.acquire()
try:
      super(extend_class, self).add(elem)
      finally:
      self._lock.release()
   def delete(self, elem):
      self._lock.acquire()
      try:
      super(extend_class, self).delete(elem)
      finally:
      self._lock.release()
在上面的示例中,定义了名为 extend_class 的类对象,该对象进一步从Python set类继承。在此类的构造函数中创建一个锁对象。现在,有两个函数- add() delete()。这些函数已定义并且是线程安全的。它们都依赖 super 类功能,但有一个关键的例外。

装饰器

这是线程安全通信的另一种关键方法是使用装饰器。

示例

考虑一个Python示例,该示例演示如何使用装饰器&mminus;
# Filename : example.py
# Copyright : 2020 By Lidihuo
# Author by : www.lidihuo.com
# Date : 2020-08-22
def lock_decorator(method):
   def new_deco_method(self, *args, **kwargs):
      with self._lock:
         return method(self, *args, **kwargs)
return new_deco_method
class Decorator_class(set):
   def __init__(self, *args, **kwargs):
      self._lock = Lock()
      super(Decorator_class, self).__init__(*args, **kwargs)
   @lock_decorator
   def add(self, *args, **kwargs):
      return super(Decorator_class, self).add(elem)
   @lock_decorator
   def delete(self, *args, **kwargs):
      return super(Decorator_class, self).delete(elem)
在上面的示例中,定义了一个名为lock_decorator的装饰器方法,该方法进一步从Python方法类继承。然后,在此类的构造函数中创建一个锁对象。现在,有两个功能-add()和delete()。这些函数已定义并且是线程安全的。它们都依赖于超类功能,但有一个关键的例外。

列表

列表数据结构是线程安全的,快速且易于存储的临时内存结构。在Cpython中,GIL防止并发访问它们。我们知道列表是线程安全的,但是其中的数据又如何呢?实际上,列表的数据不受保护。例如,如果另一个线程试图做同样的事情, L.append(x)不能保证返回预期的结果。这是因为,尽管 append()是原子操作且线程安全的,但另一个线程正在尝试以并发方式修改列表的数据,因此我们可以看到竞争条件对输出的副作用
要解决此类问题并安全地修改数据,我们必须实现适当的锁定机制,该机制进一步确保了多个线程不会潜在地陷入竞争状态。为了实现适当的锁定机制,我们可以像前面的示例中那样扩展类。
列表上的其他一些原子操作如下-
# Filename : example.py
# Copyright : 2020 By Lidihuo
# Author by : www.lidihuo.com
# Date : 2020-08-22
L.append(x)
L1.extend(L2)
x = L[i]
x = L.pop()
L1[i:j] = L2
L.sort()
x = y
x.field = y
D[x] = y
D1.update(D2)
D.keys()
这里-
L,L1,L2都是列表 D,D1,D2是字典 x,y是对象 i,j是整数

队列

如果列表的数据未受到保护,我们可能不得不面对后果。我们可能会获得或删除有关比赛条件的错误数据项。因此,建议使用队列数据结构。队列的真实示例可以是单车道单向道路,车辆首先进入,然后首先离开。在售票窗口和公交车站的队列中可以看到更多真实的例子。
队列
默认情况下,队列是线程安全的数据结构,我们不必担心实现复杂的锁定机制。 Python为我们提供了 <队列> 模块以在我们的应用程序中使用不同类型的队列。

队列类型

在本节中,我们将了解不同类型的队列。 Python在 模块中提供了三种可供使用的队列选项-
普通队列(先进先出,先进先出) 后进先出,后进先出 优先
我们将在后续部分中了解不同的队列。

普通队列(先进先出,先进先出)

它是Python提供的最常用的队列实现。在这种排队机制中,无论谁先到,都将首先获得服务。 FIFO也称为普通队列。 FIFO队列可以表示如下-
 FIFO

FIFO队列的Python实现

在python中,FIFO队列可以同时使用单线程和多线程来实现。

具有单线程的FIFO队列

要使用单线程实现FIFO队列, Queue 类将实现一个基本的先进先出容器。元素将使用 put()添加到序列的一个"末端",并使用 get()从另一末端除去。

示例

以下是用于实现单线程FIFO队列的Python程序-
# Filename : example.py
# Copyright : 2020 By Lidihuo
# Author by : www.lidihuo.com
# Date : 2020-08-22
import queue
q = queue.Queue()
for i in range(8):
   q.put("item-" + str(i))
while not q.empty():
   print (q.get(), end = " ")

输出

# Filename : example.py
# Copyright : 2020 By Lidihuo
# Author by : www.lidihuo.com
# Date : 2020-08-22
item-0 item-1 item-2 item-3 item-4 item-5 item-6 item-7
输出结果表明,上面的程序使用一个线程来说明元素按照插入顺序从队列中删除。

具有多个线程的FIFO队列

要实现具有多个线程的FIFO,我们需要定义myqueue()函数,该函数是从队列模块扩展的。使用单线程实现FIFO队列时,get()和put()方法的工作与上面讨论的相同。然后要使其成为多线程,我们需要声明并实例化线程。这些线程将以FIFO方式消耗队列。

示例

以下是用于实现具有多个线程的FIFO队列的Python程序
# Filename : example.py
# Copyright : 2020 By Lidihuo
# Author by : www.lidihuo.com
# Date : 2020-08-22
import threading
import queue
import random
import time
def myqueue(queue):
   while not queue.empty():
   item = queue.get()
   if item is None:
   break
   print("{} removed {} from the queue".format(threading.current_thread(), item))
   queue.task_done()
   time.sleep(2)
q = queue.Queue()
for i in range(5):
   q.put(i)
threads = []
for i in range(4):
   thread = threading.Thread(target=myqueue, args=(q,))
   thread.start()
   threads.append(thread)
for thread in threads:
   thread.join()

输出

<Thread(Thread-3654, started 5044)> removed 0 from the queue
<Thread(Thread-3655, started 3144)> removed 1 from the queue
<Thread(Thread-3656, started 6996)> removed 2 from the queue
<Thread(Thread-3657, started 2672)> removed 3 from the queue
<Thread(Thread-3654, started 5044)> removed 4 from the queue

LIFO,后进先出队列

此队列使用与FIFO(先进先出)队列完全相反的类比。在这种排队机制中,最后一位将首先获得服务。这类似于实现堆栈数据结构。实践证明,LIFO队列在实现深度优先搜索(如人工智能算法)时非常有用。

LIFO队列的Python实现

在python中,LIFO队列既可以使用单线程也可以使用多线程来实现。

单线程LIFO队列

要使用单线程实现LIFO队列, Queue 类将使用结构 Queue .LifoQueue实现一个基本的后进先出容器。现在,在调用 put()时,也可​​以使用 get()将元素添加到容器的头部,并从头部移除。

示例

以下是用于使用单线程实现LIFO队列的Python程序-
# Filename : example.py
# Copyright : 2020 By Lidihuo
# Author by : www.lidihuo.com
# Date : 2020-08-22
import queue
q = queue.LifoQueue()
for i in range(8):
   q.put("item-" + str(i))
while not q.empty():
   print (q.get(), end=" ")
Output:
item-7 item-6 item-5 item-4 item-3 item-2 item-1 item-0
输出显示,以上程序使用单个线程来说明以与插入元素相反的顺序从队列中删除元素。

具有多个线程的LIFO队列

实现与我们完成具有多个线程的FIFO队列的实现类似。唯一的区别是我们需要使用 Queue 类,该类将通过使用 Queue.LifoQueue 结构实现一个基本的后进先出容器。

示例

以下是用于实现具有多个线程的LIFO队列的Python程序-
# Filename : example.py
# Copyright : 2020 By Lidihuo
# Author by : www.lidihuo.com
# Date : 2020-08-22
import threading
import queue
import random
import time
def myqueue(queue):
   while not queue.empty():
      item = queue.get()
      if item is None:
      break
print("{} removed {} from the queue".format(threading.current_thread(), item))
      queue.task_done()
      time.sleep(2)
q = queue.LifoQueue()
for i in range(5):
   q.put(i)
threads = []
for i in range(4):
   thread = threading.Thread(target=myqueue, args=(q,))
   thread.start()
   threads.append(thread)
for thread in threads:
   thread.join()

输出

<Thread(Thread-3882, started 4928)> removed 4 from the queue
<Thread(Thread-3883, started 4364)> removed 3 from the queue
<Thread(Thread-3884, started 6908)> removed 2 from the queue
<Thread(Thread-3885, started 3584)> removed 1 from the queue
<Thread(Thread-3882, started 4928)> removed 0 from the queue

优先级队列

在FIFO和LIFO队列中,项的顺序与插入顺序有关。但是,在许多情况下,优先级比插入顺序更重要。让我们考虑一个真实的例子。假设机场的保安人员正在检查不同类别的人员。 VVIP的人员,航空公司人员,海关官员,类别可能会被优先检查,而不是像平民百姓一样根据到达的时间进行检查。
优先级队列需要考虑的另一个重要方面是如何开发任务计划程序。一种常见的设计是在队列中按优先级服务于最多的代理任务。此数据结构可用于根据优先级值从队列中提取项目。

优先级队列的Python实现

在python中,优先级队列既可以使用单线程也可以使用多线程来实现。

具有单线程的优先级队列

对于使用单线程实现优先级队列, Queue 类将使用结构 Queue .PriorityQueue在优先级容器上实现任务。现在,在调用 put()时,将为元素添加一个值,其中最低值具有最高优先级,因此首先使用 get()进行检索。

示例

考虑以下Python程序,以实现具有单线程的Priority队列-
# Filename : example.py
# Copyright : 2020 By Lidihuo
# Author by : www.lidihuo.com
# Date : 2020-08-22
import queue as Q
p_queue = Q.PriorityQueue()
p_queue.put((2, 'Urgent'))
p_queue.put((1, 'Most Urgent'))
p_queue.put((10, 'Nothing important'))
prio_queue.put((5, 'Important'))
while not p_queue.empty():
   item = p_queue.get()
   print('%s - %s' % item)

输出

1 – Most Urgent
2 - Urgent
5 - Important
10 – Nothing important
在上面的输出中,我们可以看到队列根据优先级存储了项目-值越小优先级越高。

具有多线程的优先级队列

该实现类似于具有多个线程的FIFO和LIFO队列的实现。唯一的区别是,我们需要使用 Queue 类通过使用 Queue.PriorityQueue 结构来初始化优先级。另一个区别是生成队列的方式。在下面给出的示例中,它将使用两个相同的数据集生成。

示例

以下Python程序可帮助实现具有多个线程的优先级队列-
# Filename : example.py
# Copyright : 2020 By Lidihuo
# Author by : www.lidihuo.com
# Date : 2020-08-22
import threading
import queue
import random
import time
def myqueue(queue):
   while not queue.empty():
      item = queue.get()
      if item is None:
      break
      print("{} removed {} from the queue".format(threading.current_thread(), item))
      queue.task_done()
      time.sleep(1)
q = queue.PriorityQueue()
for i in range(5):
   q.put(i,1)
for i in range(5):
   q.put(i,1)
threads = []
for i in range(2):
   thread = threading.Thread(target=myqueue, args=(q,))
   thread.start()
   threads.append(thread)
for thread in threads:
   thread.join()

输出

<Thread(Thread-4939, started 2420)> removed 0 from the queue
<Thread(Thread-4940, started 3284)> removed 0 from the queue
<Thread(Thread-4939, started 2420)> removed 1 from the queue
<Thread(Thread-4940, started 3284)> removed 1 from the queue
<Thread(Thread-4939, started 2420)> removed 2 from the queue
<Thread(Thread-4940, started 3284)> removed 2 from the queue
<Thread(Thread-4939, started 2420)> removed 3 from the queue
<Thread(Thread-4940, started 3284)> removed 3 from the queue
<Thread(Thread-4939, started 2420)> removed 4 from the queue
<Thread(Thread-4940, started 3284)> removed 4 from the queue
昵称: 邮箱:
Copyright © 2022 立地货 All Rights Reserved.
备案号:京ICP备14037608号-4