Django 1.6交易避免了竞争条件

Ale*_*lho 7 python django multithreading transactions race-condition

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

我目前的做法是:

  1. 用户想玩
  2. 服务器检查是否还有其他人在等待播放.
    1. 如果没有,则创建一个GameConnection对象(具有唯一标识符 - uuid4).
    2. 如果,它得到了GameConnection识别并删除GameConnection.

这是代码:

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

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

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

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

我相信我在这里缺少一些交易动态.任何人都可以光明吗?

bim*_*api 2

@Sai 正在步入正轨......关键是锁定/互斥体在写入(或删除)之前不会发生。按照编码,在挂起连接的“发现”(读取)和挂起连接的“声明”(写入/锁定)之间始终存在一段时间,无法知道连接是否正在声明过程中。

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

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

最后注意事项 - 除了all()明确说明可能会选择哪个游戏(例如,按“创建”时间戳或其他内容排序)之外,请考虑其他事项。希望有帮助。