python - 为什么这段 python 代码不是线程安全的?

标签 python multithreading python-3.x concurrency

我把它作为学校作业的一部分交给了它,标记它的人提到这部分不是线程安全的。

任务是在 python 中创建一个多线程套接字服务器,它接受一个数字并返回该数字的斐波那契值。我的方法是通过在每个线程之间共享字典来内存计算。

这是代码(为了简洁起见删除了错误处理)

from socketserver import ThreadingMixIn, TCPServer, BaseRequestHandler


class FibonacciThreadedTCPServer(ThreadingMixIn, TCPServer):
    def __init__(self, server_address):
        TCPServer.__init__(self, server_address, FibonacciThreadedTCPRequestHandler, bind_and_activate=True)
        #this dictionary will be shared between all Request handlers
        self.fib_dict = {0: 0, 1: 1, 2: 1}


class FibonacciThreadedTCPRequestHandler(BaseRequestHandler):
    def handle(self):
        data = self.request.recv(1024).strip()
        num = int(data)
        result = self.calc_fib(self.server.fib_dict, num)
        ret = bytes(str(result) + '\n', 'ascii')
        self.request.sendall(ret)


    @staticmethod
    def calc_fib(fib_dict, n):
        """
        Calculates the fibonacci value of n using a shared lookup table and a linear calculation.
        """
        length = len(fib_dict)
        while length <= n:
            fib_dict[length] = fib_dict[length - 1] + fib_dict[length - 2]
            length = len(fib_dict)
        return fib_dict[n]

我知道在 calc_fib 方法中同时发生读取和写入,通常这意味着代码不是线程安全的。然而,在这种情况下,我认为有可能证明代码将始终提供可预测的结果。

读取和写入可以同时发生这一事实足以使其不被视为线程安全的吗?或者如果它总是返回可靠的结果,那么它被认为是线程安全的。

为什么我认为这段代码总是会产生可靠的结果:

  1. 在字典中的任何给定索引上发生写入之前,永远不会发生读取。

  2. 对任何给定索引的任何后续写入都将包含与先前写入相同的数字,因此无论读/写序列何时发生,它始终会收到相同的数据。

我通过在每个操作之间添加随机 sleep 并同时向数百个线程发出请求来测试这一点,并且在我的测试期间已经返回了正确的答案。

任何想法或批评将不胜感激。谢谢。

最佳答案

在这种特殊情况下,the GIL应该确保您的代码安全,因为:

  1. CPython 内置数据结构受到 GIL(dict 特别需要这种保证,因为类实例和非本地范围通常使用 dict 进行属性/名称查找,如果没有 GIL,简单地读取值将充满危险)
  2. 您正在更新长度的缓存值,然后将其用于下一组操作,而不是在更改期间重新检查长度;这可能会导致重复工作(多个线程查看旧长度并重复计算新值)但由于键始终设置为相同的值,因此它们是否各自独立设置并不重要
  3. 你永远不会从你的缓存中删除(如果你这样做,缓存的长度会咬你)

所以在 CPython 中,这应该没问题。不过,我不能对其他 Python 解释器做出任何保证;没有 GIL,如果他们在没有内部锁定的情况下实现他们的 dict,则完全有可能由一个线程中的写入触发的重新哈希操作可能导致另一个线程从 dict 中读取不一致/不可用状态。

关于python - 为什么这段 python 代码不是线程安全的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42564437/

相关文章:

python - 使用 csv.reader 和 csv.writer 写入同一文件而不是不同的文件?

python - 帮助使用带有循环的 Python 数独验证器

python - 通过 Python 版本制作 `nosetests` 脚本选择文件夹

python - 如何在 Python 中使用类型注释语法声明多个变量?

Python gzip 模块在 ubyte 文件上无法按预期工作

python - 来自 Hive 查询的持久 PySpark Dataframe

python - 如何编写一个接受列表并返回不带元音的相同列表的递归函数?

android - 为什么 Handler::postDelayed 会使 UI 卡住

c++ - 共享列表、多个条件、一个或多个条件变量?

c# - 一键访问两个线程功能