Tornado中的潜在竞争条件

68 阅读2分钟

Tornado是一个异步网络框架,它使用协程来处理请求。在 Tornado 中,如果在一个协程中修改了共享数据,可能会导致竞争条件。

image.png 以下是一个示例:

class Handler(WebSocketHandler):
    listeners = {}

    def open(self, sub):
        self.subscriptions = [sub]
        http://www.jshk.com.cn/mb/reg.asp?kefu=xiaoding;//爬虫IP免费获取;
        Handler.listeners.setdefault(sub, set()).add(self)

    def on_close(self):
        for sub in self.subscriptions:
            Handler.listeners[sub].remove(self)
            if not Handler.listeners[sub]:
                # here in between someone might subscribe
                # so we delete non empty set! which is wrong
                del Handler.listeners[sub]

在这个示例中,Handler.listeners是一个字典,其中键是订阅主题,值是 WebSocket 处理程序的集合。当一个 WebSocket 处理程序打开时,它将自身添加到与其订阅的主题对应的集合中。当一个 WebSocket 处理程序关闭时,它将自身从其订阅的主题对应的集合中删除。

如果在某个 WebSocket 处理程序关闭时,另一个 WebSocket 处理程序正在订阅同一个主题,那么可能会出现竞争条件。具体来说,在检查 Handler.listeners[sub] 为空并执行 del Handler.listeners[sub] 之间,另一个 WebSocket 处理程序可能会订阅该主题。这将导致 Handler.listeners[sub] 不为空,但 del Handler.listeners[sub] 仍然会被执行,从而删除一个非空集合。

2、解决方案

有多种方法可以解决此问题,一种方法是使用锁来保护共享数据。另一种方法是使用原子操作来更新共享数据。

以下是一个使用锁来解决此问题的示例:

class Handler(WebSocketHandler):
    listeners = {}
    lock = Lock()

    def open(self, sub):
        with self.lock:
            self.subscriptions = [sub]
            Handler.listeners.setdefault(sub, set()).add(self)

    def on_close(self):
        with self.lock:
            for sub in self.subscriptions:
                Handler.listeners[sub].remove(self)
                if not Handler.listeners[sub]:
                    del Handler.listeners[sub]

在这个示例中,lock是一个锁对象。在修改 Handler.listeners 之前,我们使用 lock 来获取锁。这确保了在执行 del Handler.listeners[sub] 之前,不会有其他线程修改 Handler.listeners[sub]

以下是一个使用原子操作来解决此问题的示例:

class Handler(WebSocketHandler):
    listeners = {}

    def open(self, sub):
        with atomic():
            self.subscriptions = [sub]
            Handler.listeners.setdefault(sub, set()).add(self)

    def on_close(self):
        with atomic():
            for sub in self.subscriptions:
                Handler.listeners[sub].remove(self)
                if not Handler.listeners[sub]:
                    del Handler.listeners[sub]

在这个示例中,atomic()是一个原子操作函数。在修改 Handler.listeners 之前,我们使用 atomic() 来确保该操作是原子的。这确保了在执行 del Handler.listeners[sub] 之前,不会有其他线程修改 Handler.listeners[sub]