我试图使用Django 1.6交易来避免我正在开发的游戏中的竞争条件。游戏服务器有一个简单的目标:配对两个玩家。
我目前的做法是:
这是代码:
# 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
显然上面的代码存在竞争条件。由于此代码不是原子代码,因此可能会发生:
我尝试了with transaction.atomic():
下的整个块,或使用@transaction.atomic
装饰器。但是,我仍然可以重现竞争条件。
我确信我在这里缺少一些交易动态。任何人都可以发光吗?
答案 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()
以外的其他内容,明确哪些游戏可能被选中(例如,通过“创建”时间戳或其他内容订购)。希望有所帮助。