3

我在 Web 应用程序中面临潜在的竞争条件:

# get the submissions so far from the cache
submissions = cache.get('user_data')
# add the data from this user to the local dict
submissions[user_id] = submission

# update the cached dict on server
submissions = cache.update('user_data', submissions)

if len(submissions) == some_number:
    ...

逻辑很简单,我们首先获取存储在 web 服务器缓存中的共享字典,将提交(由每个请求传递到服务器)添加到其本地副本,然后我们通过将其替换为更新的本地副本来更新缓存副本复制。最后,如果我们收到一定数量的数据,我们会做其他事情。请注意

submissions = cache.update('user_data', submissions)

将从缓存中返回最新的字典副本,即新更新的字典。

因为服务器可能同时服务多个请求(每个请求在自己的线程中),并且所有这些线程都访问缓存中的共享字典,如上所述,从而产生潜在的竞争条件。

我想知道,在 Web 编程的上下文中,我应该如何有效地处理线程以防止在这种特殊情况下出现竞争条件,而不会牺牲太多的性能。一些代码示例将不胜感激。

4

3 回答 3

2

我首选的解决方案是拥有一个修改提交字典的线程和一个为该线程提供数据的队列。如果你是偏执狂,你甚至可以在提交的字典上公开一个只读视图。使用队列和消费者模式,您不会遇到锁定问题。

当然,这假设您有一个可以让您创建该线程的 Web 框架。

编辑:多进程不是一个好建议;删除。

编辑:这种东西在 Python 中非常简单:

import threading, Queue

Stop = object()

def consumer(real_dict, queue):
    while True:
        try:
            item = queue.get(timeout=100)
            if item == Stop:
                break
            user, submission = item
            real_dict[user] = submission
        except Queue.Empty:
            continue

q = Queue.Queue()
thedict={}

t = threading.Thread(target=consumer, args=(thedict,q,))
t.start()

然后,您可以尝试:

>>> thedict
{}
>>> q.put(('foo', 'bar'))
>>> thedict
{'foo': 'bar'}
>>> q.put(Stop)
>>> q.put(('baz', 'bar'))
>>> thedict
{'foo': 'bar'}
于 2012-05-08T18:24:05.633 回答
1

您似乎在 Web 应用程序和缓存之间来回传输大量数据。这已经是个问题了。你的怀疑也是对的,因为模式可能是这样的(记住这sub是每个线程的本地):

线程 A 线程 B 缓存
------------------------------------------
                                [A]=P,[B]=Q
子 = 得到()
   [A]=P,[B]=Q
>>>> 暂停
                子 = 得到()
                   [A]=P,[B]=Q
                子[B] = Y
                   [A]=P,[B]=Y
                更新(子)
                                [A]=P,[B]=Y
                >>>> 暂停
子[A] = X
   [A]=X,[B]=Q
更新(子)
                                [A]=X, [B]=Q!!!!!!!

这种模式可能会真实发生,并导致状态消失。这也是低效的,因为线程 A 通常只需要了解其当前用户,而不是所有内容。

虽然你可以通过大量的锁定来解决这个问题,但这将是非常低效的。因此,您需要重新设计,以便传输更少的数据,这将提高性能并减少您需要的锁定量。

于 2012-05-08T18:26:40.947 回答
1

这是更难回答的问题之一,因为它似乎是一个更大的设计问题。

这个问题的一个潜在解决方案是有一个明确定义的地方来更新它。例如,您可能希望设置另一个专门用于更新缓存的服务,而不是其他服务。或者,如果这些更新对时间不敏感,您可能还需要考虑使用任务队列。

另一种解决方案:您可以给每个项目一个单独的密钥,并将密钥列表存储在一个单独的密钥下。这不一定能解决问题,但它确实使它更易于管理。您不必担心单独的线程会覆盖整个提交缓存,而只需担心它们会覆盖其中的单个元素。

如果您有时间为您的基础架构添加一个新部分,我强烈建议您查看Redis,更具体地说是Redis 哈希[1]。原因是 Redis 开箱即用地处理了这个问题,其速度与使用 memcache 获得的速度大致相同(尽管我绝对鼓励您自己进行基准测试)。

[1] 注意:我只是通过快速谷歌搜索找到了这个链接,还没有验证它。我不保证它的正确性。

于 2012-05-08T18:49:16.090 回答