【问题标题】:Which approach is best for thread safe global storage for multithreaded environments in python (CPython)?哪种方法最适合python(CPython)中多线程环境的线程安全全局存储?
【发布时间】:2021-07-31 06:30:05
【问题描述】:

我有如下要求:

global_storage = "some_global_storage_system"

def call_me():
    global_storage += 1
    if global_storage > 1000:
        with threading.Lock():
            global_storage = 0 # reset global storage
            # performing some action
    

以上代码可以同时被5个线程访问

我找到了很多这样做的方法,但没有将它们收集在一个地方,并且没有提到以下哪一种是在上述程序中实现 global_storage 之类的最佳和最安全的方法:

  1. 使用 python queues 库(因为它是线程安全的,但可能是内存问题):每次将一个项目放入队列中,并检查队列长度何时变为 1000,然后再次将队列长度设为 0。
  2. 在全局级别使用dictionary(Cpython 中的线程安全):制作字典global_dict["count"]=0,每次更新global_dict["count"]++,然后读取global_dict["count"]>1000
  3. 使用global 变量,使用global 关键字(社区似乎最不推荐的关键字)
  4. 使用redis(避免,因为它是网络调用的额外负担):redis.set() 开始,redis.get() 获取值
  5. 使用threading.local 对象(感觉是最安全的对象),但我可能需要将检查计数减少到200 以达到相同的结果。

【问题讨论】:

    标签: python multithreading multiprocessing thread-safety gil


    【解决方案1】:

    您需要意识到操作global_storage += 1 可能不是在您想到的任何“some_global_storage_system”的几乎任何实现中都是原子操作。如果global_storageint,它肯定不是原子的,而且你不能得到比这更基本的东西。这意味着这个操作也需要在锁下进行序列化。

    在下面的代码中,我在全局范围内创建了一个名为 global_storage_lockthreading.Lock 实例,所有线程都可以访问并使用它来序列化对 global_storage 的访问。我对您的唯一(修辞)问题涉及您标记为#performing some action 的评论,您当前在获得锁定时拥有该评论。通常,您希望在尽可能短的时间内保持锁定。如果您不需要在此操作期间更新global_storage,则在此锁定将被释放的块之外执行该操作。

    import threading
    
    global_storage_lock = threading.Lock()
    global_storage = "some_global_storage_system"
    
    def call_me():
        with global_storage_lock:
            global_storage += 1
            if global_storage > 1000:
                global_storage = 0 # reset global storage
                # performing some action
    

    下面是一个示例,说明在将 global_storage 重置为 0 后需要执行操作时,如何处理不需要保持获取锁的情况:

    import threading
    
    global_storage_lock = threading.Lock()
    global_storage = "some_global_storage_system"
    
    def call_me():
        action_needed = False
        with global_storage_lock:
            global_storage += 1
            if global_storage > 1000:
                global_storage = 0 # reset global storage
                action_needed = True
        if action_needed:
            #performing some action
            ...
    

    【讨论】:

    • 知道了。我还有一个疑问。是否有可能实现这样的东西,只有可以调用call_me(可能是特定线程)的one of the multiple threads才能执行#performing some action的操作?
    • 您最后的评论/问题需要更多详细信息。您最好提出一个新问题并更清楚地描述您将如何确定哪个线程获得能够运行该操作的特权以及在什么条件下。
    猜你喜欢
    • 1970-01-01
    • 2011-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-16
    • 2013-12-23
    • 1970-01-01
    相关资源
    最近更新 更多