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

5 python concurrency multithreading python-3.x

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

分配是在python中创建一个多线程套接字服务器,接受一个数字并返回该数字的Fibonacci值.我的方法是通过在每个线程之间共享字典来记忆计算.

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

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]
Run Code Online (Sandbox Code Playgroud)

我知道在calc_fib方法中同时发生了读写操作,通常这意味着代码不是线程安全的.但是在这种情况下,我认为可以证明代码将始终提供可预测的结果.

事实上,读取和写入可以同时发生,因为它不被认为是线程安全的吗?或者,如果它总是返回可靠的结果,则被认为是线程安全的.

为什么我认为此代码将始终产生可靠的结果:

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

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

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

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

Sha*_*ger 4

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

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

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