如果我的资源更新方法也创建了不同类型的资源,它是否是线程安全的?

2024-09-30 01:34:26 发布

您现在位置:Python中文网/ 问答频道 /正文

tastype文档声明绑定keep Tastypie more thread-safe,但没有解释如何以及在什么条件下。我已经看过了code,但是经验不足,没有足够的经验来围着它转。在

我正在制作一个游戏的原型,它有一个圆形对象(对于每一轮游戏)和每轮的多个状态(对于每一轮的每个玩家的信息)。每个玩家都会更新自己的状态,并给出回合词短语的答案。我需要一种机制,如果还不存在的话,它会懒洋洋地创造下一轮比赛。当玩家更新他们的状态时,我会触发这个回合的创建。在

如果多个玩家同时更新他们的状态(见StateResource.obj_update()),那么他们试图制造下一轮的碰撞会不会发生?我认为如果一个obj_update调用检查下一轮是否存在,并在另一个obj_update完成下一轮创建之前尝试创建下一轮,则可能会发生这种情况。我会用某种类型的互斥来解决这个问题,但我不确定是否有必要。我想知道是否有一个典型的方法来解决这个问题。在

我的代码如下:

#models.py
class Round(models.Model):
    game_uid = models.CharField(max_length=75)
    word = models.CharField(max_length=75)
    players = models.ManyToManyField(User)
    next_round = models.OneToOneField('self',null=True,blank=True)

class PlayerRoundState(models.Model):
    player = models.ForeignKey(User)
    round = models.ForeignKey(Round)
    answer = models.CharField(max_length=75)

#api.py
class RoundResource(ModelResource):
    players = fields.ManyToManyField(UserResource, attribute='players',full=False)
    states = fields.ManyToManyField('wordgame.api.StateResource',
                                attribute='playerroundstate_set',
                                full=True)
    . . .
    def obj_create(self, bundle, request=None, **kwargs):
        bundle = super(RoundResource, self).obj_create(bundle, request,**kwargs)
        bundle.obj.word = choice(words) #Gets a random word from a list
        bundle.obj.round_number = 1
        bundle.obj.game_uid = bundle.obj.calc_guid() #Creates a unique ID for the game
        bundle.obj.save()
        return bundle

class StateResource(ModelResource):
    player = fields.ForeignKey(UserResource, 'player',full=False)
    round = fields.ForeignKey(RoundResource, 'round')
    . . . 
    def obj_update(self, bundle, request=None, skip_errors=False, **kwargs):
        bundle = super(StateResource, self).obj_update(bundle, request,
                                                   skip_errors, **kwargs)
        if bundle.obj.round.next_round is None:
            new_round = Round()
            new_round.word = choice(words)
            new_round.round_number = bundle.obj.round.round_number + 1
            new_round.game_uid = bundle.obj.round.game_uid
            new_round.save()
            for p in bundle.obj.round.players.all():
                new_round.players.add(p)
            new_round.save()
            bundle.obj.round.next_round = new_round
            bundle.obj.round.save()

        return bundle

Tags: selfgameobjnewuidmodels状态玩家
1条回答
网友
1楼 · 发布于 2024-09-30 01:34:26

我觉得这跟品味没什么关系。在

您所描述的问题与ORM和数据库有关。{gen}在某些情况下,{grn>在一个新的情况下,都可以创建一个新的请求,如果在这个情况下,^都是新的,那么这两个请求都可能在一个时间内被处理。在

考虑以下情况:

第一个请求到达,检索当前轮并“看到”没有“下一轮”。所以它表现为:

new_round = Round()
new_round.word = choice(words)
new_round.round_number = bundle.obj.round.round_number + 1
new_round.game_uid = bundle.obj.round.game_uid
new_round.save()

同时,第二个请求来了(假设在您的设置中是可能的)处理切换到第二个请求。它还检索当前轮,并且“看到”没有下一轮,因此它也创建了一个(同一逻辑轮的第二个对象)。在

然后,处理切换回执行以下操作的第一个请求:

^{pr2}$

所以现在有了下一轮。第一个请求被处理,一切看起来都很好。 但是第二个请求还没有完成,它执行同样的操作,覆盖当前的round对象。在

结果是您有一个过时的实例(由第一个请求创建的Round),并且第一组玩家使用与第二组不同的Round。在

这会导致数据库中的状态不一致。所以在这种情况下,您的资源更新方法是线程安全的。在

一种解决方案是使用select_for_update从数据库中检索当前回合。见Django Docs。如果使用该方法,则第二个和连续的请求将一直等到您在第一个请求中修改当前轮,然后才从数据库中检索它。结果将是,他们已经“看到”下一轮,而不是试图创造它。当然,您必须确保整个更新构成一个事务。在

“使用”的方法是重写StateResource资源中的obj_get()方法,而不是:

base_object_list = self.get_object_list(request).filter(**kwargs)

使用(未测试):

base_object_list = self.get_object_list(request).select_for_update().filter(**kwargs)

当然,这不是唯一的解决方案,但其他解决方案可能会涉及到重新设计应用程序,因此这可能会减少涉及。在

相关问题 更多 >

    热门问题