Link to this section使用 YOLO 模型进行线程安全推理#
若要在 Python 线程中安全地运行 Ultralytics YOLO 推理,请在每个线程内实例化一个独立的 YOLO 模型,而不是在线程间共享同一个实例。由于 Python 的 threading 模块会使多个线程并发访问同一个对象,共享单个模型会导致竞态条件,从而破坏其内部状态并产生不可预测的结果。本指南解释了为何共享会导致失败,展示了每个线程使用独立实例的安全模式,并介绍了在必须共享实例时可使用的 ThreadingLocked 装饰器。
跳转至 为何共享模型会失败、线程安全模式 或 ThreadingLocked 装饰器。
Watch: How to Perform Thread Safe Inference with Ultralytics YOLO Models in Python | Multi-Threading 🚀
Link to this section了解 Python 多线程#
Python 线程是一种并行形式,允许你的程序同时运行多个操作。然而,Python 的全局解释器锁 (GIL) 意味着在任何时候只能有一个线程执行 Python 字节码。
虽然这听起来像是一个限制,但线程仍然可以提供并发性,特别是在处理 I/O 密集型操作时,或者在使用释放 GIL 的操作(如 YOLO 底层 C 库执行的操作)时。
Link to this section共享模型实例的危险性#
在线程外部实例化 YOLO 模型并在多个线程之间共享该实例,可能会导致竞态条件,即模型的内部状态由于并发访问而被不一致地修改。当模型或其组件持有的状态未被设计为线程安全时,这一点尤为突出。
Link to this section非线程安全示例:单模型实例#
在使用 Python 线程时,识别可能导致并发问题的模式非常重要。请避免以下操作:在多个线程间共享单个 YOLO26 模型实例。
# Unsafe: Sharing a single model instance across threads
from threading import Thread
from ultralytics import YOLO
# Instantiate the model outside the thread
shared_model = YOLO("yolo26n.pt")
def predict(image_path):
"""Predicts objects in an image using a preloaded YOLO model, take path string to image as argument."""
results = shared_model.predict(image_path)
# Process results
# Starting threads that share the same model instance
Thread(target=predict, args=("image1.jpg",)).start()
Thread(target=predict, args=("image2.jpg",)).start()在上面的示例中,shared_model 被多个线程使用,这可能导致不可预知的结果,因为 predict 可能会被多个线程同时执行。
Link to this section安全示例:每个线程使用专用实例#
只要每个线程拥有自己的实例且从不与其他线程共享,创建多个独立模型实例是没有问题的。无论下方实例是在线程启动前创建的都无关紧要——唯一不安全的模式是在线程间共享同一个实例:
# Safe: each thread uses its own dedicated model instance
from threading import Thread
from ultralytics import YOLO
# Instantiate one model per thread
model_1 = YOLO("yolo26n.pt")
model_2 = YOLO("yolo26n.pt")
def predict(model, image_path):
"""Runs prediction on an image using a specified YOLO model, returning the results."""
results = model.predict(image_path)
# Process results
# Each thread uses a separate, dedicated model instance
Thread(target=predict, args=(model_1, "image1.jpg")).start()
Thread(target=predict, args=(model_2, "image2.jpg")).start()由于每个线程都在使用其专属实例,因此不存在线程可能破坏的共享模型状态。如下所示,在每个线程内实例化模型是确保实例绝不会被意外共享的最简单方法。
Link to this section线程安全推理#
要执行线程安全推理,你应该在每个线程内实例化一个单独的 YOLO 模型。这确保了每个线程都有自己独立的模型实例,从而消除了竞态条件的风险。
Link to this section线程安全示例#
以下是如何在每个线程内实例化 YOLO 模型以进行安全并行推理的方法:
# Safe: Instantiating a single model inside each thread
from threading import Thread
from ultralytics import YOLO
def thread_safe_predict(image_path):
"""Predict on an image using a new YOLO model instance in a thread-safe manner; takes image path as input."""
local_model = YOLO("yolo26n.pt")
results = local_model.predict(image_path)
# Process results
# Starting threads that each have their own model instance
Thread(target=thread_safe_predict, args=("image1.jpg",)).start()
Thread(target=thread_safe_predict, args=("image2.jpg",)).start()在这个示例中,每个线程都会创建自己的 YOLO 实例。这防止了任何线程干扰另一个线程的模型状态,从而确保每个线程都能安全地进行推理,且不会与其他线程产生意外交互。
Link to this section使用 ThreadingLocked 装饰器#
Ultralytics 提供了一个 ThreadingLocked 装饰器,可用于确保函数的线程安全执行。此装饰器使用锁来保证同一时间只有一个线程能够执行被装饰的函数。
from ultralytics import YOLO
from ultralytics.utils import ThreadingLocked
# Create a model instance
model = YOLO("yolo26n.pt")
# Decorate the prediction function to make it thread-safe
@ThreadingLocked()
def thread_safe_predict(image_path):
"""Thread-safe prediction using a shared model instance."""
results = model.predict(image_path)
return results
# Now you can safely call this function from multiple threads当需要在线程间共享模型实例但又要确保同一时间只有一个线程能够访问它时,ThreadingLocked 装饰器尤其有用。
与在每个线程中加载模型相比,共享一个被锁定的模型实例可以节省内存,但由于线程会在锁上序列化并排队等待,这会降低并发性。当你有足够的内存且希望实现最大并行度时,建议优先使用每个线程独立实例的模式;当模型内存成为瓶颈时,则使用 ThreadingLocked。
Link to this section结论#
在使用 Python 的 threading 运行 YOLO 模型时,请为每个线程提供其专属的模型实例,切勿在线程间共享实例。在需要使用模型的线程内对其进行实例化是保证这一点的最简单方法,这能避免竞态条件并保持推理任务的可靠性。
对于更高级的场景,以及为了进一步优化你的多线程推理性能,请考虑使用基于进程的并行化(multiprocessing)或利用带有专用工作进程的任务队列。
Link to this section常见问题解答#
Link to this section在使用多线程 Python 环境运行 YOLO 模型时,我该如何避免竞态条件?#
为了在使用 Ultralytics YOLO 模型的多线程 Python 环境中防止竞态条件,请在每个线程内实例化一个单独的 YOLO 模型。这确保了每个线程都有自己独立的模型实例,避免了模型状态的并发修改。
示例:
from threading import Thread
from ultralytics import YOLO
def thread_safe_predict(image_path):
"""Predict on an image in a thread-safe manner."""
local_model = YOLO("yolo26n.pt")
results = local_model.predict(image_path)
# Process results
Thread(target=thread_safe_predict, args=("image1.jpg",)).start()
Thread(target=thread_safe_predict, args=("image2.jpg",)).start()有关确保线程安全的更多信息,请访问 使用 YOLO 模型进行线程安全推理。
Link to this section在 Python 中运行多线程 YOLO 模型推理的最佳实践是什么?#
要在 Python 中安全地运行多线程 YOLO 模型推理,请遵循以下最佳实践:
- 在每个线程内实例化 YOLO 模型,而不是在线程间共享单个模型实例。
- 使用 Python 的
multiprocessing模块进行并行处理,以避免与全局解释器锁(GIL)相关的问题。 - 请记住,YOLO 底层的 C 库(PyTorch, OpenCV)在进行繁重计算时会自动释放 GIL,因此线程仍然可以并发运行推理。
- 当内存受限时,可以考虑对共享模型实例使用
ThreadingLocked装饰器。
线程安全模型实例化的示例:
from threading import Thread
from ultralytics import YOLO
def thread_safe_predict(image_path):
"""Runs inference in a thread-safe manner with a new YOLO model instance."""
local_model = YOLO("yolo26n.pt")
results = local_model.predict(image_path)
# Process results
# Initiate multiple threads
Thread(target=thread_safe_predict, args=("image1.jpg",)).start()
Thread(target=thread_safe_predict, args=("image2.jpg",)).start()如需更多背景信息,请参阅 线程安全推理 部分。
Link to this section为什么每个线程都应该拥有自己的 YOLO 模型实例?#
每个线程都应该拥有自己的 YOLO 模型实例,以防止竞态条件。当单个模型实例在多个线程之间共享时,并发访问可能导致不可预知的行为以及模型内部状态的修改。通过使用单独的实例,你可以确保线程隔离,从而使你的多线程任务可靠且安全。
如需详细指南,请查看 非线程安全示例:单模型实例 和 线程安全示例 部分。
Link to this sectionPython 的全局解释器锁 (GIL) 如何影响 YOLO 模型推理?#
Python 的全局解释器锁 (GIL) 允许在同一时间只有一个线程执行 Python 字节码,这可能会限制 CPU 密集型多线程任务的性能。然而,对于 I/O 密集型操作,或者使用释放 GIL 的库(如 YOLO 的底层 C 库)的进程,你仍然可以实现并发。为了提高性能,请考虑使用 Python multiprocessing 模块进行基于进程的并行化。
有关 Python 多线程的更多信息,请参阅 了解 Python 多线程 部分。
Link to this section对于 YOLO 模型推理,使用基于进程的并行化比线程更安全吗?#
是的,使用 Python 的 multiprocessing 模块在并行运行 YOLO 模型推理时更安全,通常也更高效。基于进程的并行化创建了独立的内存空间,避免了全局解释器锁 (GIL),并降低了并发问题的风险。每个进程将独立运行,拥有自己的 YOLO 模型实例。
有关 YOLO 模型基于进程并行化的更多详细信息,请参考 线程安全推理 页面。