Django 1.6交易避免了竞争条件

时间:2014-05-09 14:45:53

标签: python django multithreading transactions race-condition

我试图使用Django 1.6交易来避免我正在开发的游戏中的竞争条件。游戏服务器有一个简单的目标:配对两个玩家。

我目前的做法是:

  1. 用户想玩
  2. 服务器检查是否还有其他人在等待播放。
    1. 如果不是,则会创建一个GameConnection对象(具有唯一标识符 - uuid4)。
    2. 如果 ,则会获取GameConnection标识符并删除GameConnection。
  3. 这是代码:

    # data['nickname'] = user's choice
    games = GameConnection.objects.all()
    if not games:
        game = GameConnection.objects.create(connection=unicode(uuid.uuid4()))
        game.nick1 = data["nickname"]
        game.save()
    
        response = HttpResponse(json.dumps({'connectionId': game.connection, 'whoAmI': 1,  'nick1': game.nick1, 'nick2': ""}))
    else:
        game = games[0]
        conn = game.connection
        nick1 = game.nick1
        nick2 = data["nickname"]
        game.delete()
        response = HttpResponse(json.dumps({'connectionId': conn, 'whoAmI': 2,  'nick1': nick1, 'nick2': nick2}))
    
    return response
    

    显然上面的代码存在竞争条件。由于此代码不是原子代码,因此可能会发生:

    • 检查游戏连接。找不到。
    • A创建游戏连接。
    • B检查游戏连接。找到一个(A)。
    • C检查游戏连接。找到一个(A)。
    • B获取A的连接标识符并开始游戏。
    • C获取A的连接标识符并开始游戏。

    我尝试了with transaction.atomic():下的整个块,或使用@transaction.atomic装饰器。但是,我仍然可以重现竞争条件。

    我确信我在这里缺少一些交易动态。任何人都可以发光吗?

1 个答案:

答案 0 :(得分:2)

@Sai正在进行中......关键是在写入(或删除)之前不会发生锁定/互斥锁。在编码时,待定连接的“发现”(读取)和待处理连接的“声明”(写入/锁定)之间总会有一段时间,无法知道连接是否在被声明的过程中。

如果您正在使用PostgreSQL(非常确定MySQL也支持它),您可以使用“select for update”强制锁定,这将阻止另一个请求在事务完成之前获取同一行:

game = GameConnection.objects.all()[:1].select_for_update()
if game:
    #do something, update, delete, etc.
else:
    #create

最后的注意事项 - 考虑all()以外的其他内容,明确哪些游戏可能被选中(例如,通过“创建”时间戳或其他内容订购)。希望有所帮助。