Как решить проблему гонки между вызовами функций

Я построил многопользовательскую игру (а точнее, 4 игрока), используя конструкцию передачи сообщений erlang. В качестве примера я следовал игре tictactoe по следующей ссылке, но на самом деле похожа конструкция передачи сообщений, как показано в игре: ссылка

Затем я решил запустить эту игру в многопользовательском чате ejabberd, для этого я написал хук ejabberd. Но если вы посмотрите на NewGameState в файле tictactoe.erl по приведенной выше ссылке, вы обнаружите, что нет способа извлечь его из переменной.

Поэтому я использовал mnesia и записывал каждое новое сгенерированное состояние игры в эту таблицу mnesia. Теперь внутри моего хука ejabberd я вызываю свою игровую функцию (т.е. при каждом вызове выполняется серия модулей -> "gen_server, game_modules,mnesia_modules"), а внутри хука чуть ниже вызова игровой функции, которую я читаю из таблицы мнезий для Gamestate выглядит следующим образом (здесь функция myMessage является функцией внутри хука ejabberd):

myMessage({#message = Msg, C2SState})->
    some_other_module:game_func(Args),
    State=mnesia_module:read(key),

    {Msg, C2SState};
myMessage(Acc) ->
    Acc.

Теперь моя проблема в том, что операция чтения дает мне пустую таблицу, когда порядок выполнения

some_other_module:game_func(Args),
 GameState=mnesia_module:read(key),

и когда я вставляю задержку между этими двумя строками как timer:sleep/1 как показано ниже (значение 200 выбирается случайным образом после некоторого испытания с другими значениями):

some_other_module:game_func(Args),
timer:sleep(200)
 GameState=mnesia_module:read(key),

Я получаю правильное значение GameState, таким образом, предлагая мне, чтобы операция чтения в строке

GameState=mnesia_module:read(key),

выполняется / выполняется перед строкой some_other_module:game_func(Args) (который представляет собой серию модулей -> "gen_server, game_modules,mnesia_modules") может выполнять модули mnesia и записывать GameState в таблицу mnesia.

Как я могу решить эту проблему, поскольку я не хочу использовать timer:sleep/1 как это не надежное решение.

Может ли кто-нибудь предложить мне работу здесь. Что я имею в виду, может ли кто-нибудь предложить мне способ получить GameState внутри хука любым другим способом, кроме mnesia, чтобы у меня вообще не было состояния гонки.

Или есть какой-то способ, которым ejabberd предоставляет некоторые функции, которые я могу использовать здесь?

Заранее спасибо.

2 ответа

Решение

Я пытаюсь дать решение, которое сработало для меня. Надеюсь, это поможет кому-то.

Вот что я сделал:

Сначала я убрал мнезию с картинки.

Я впервые зарегистрировал PID базового модуля, как только он будет создан внутри start/2 функция (вы можете думать о tictactoe.erl присутствует по ссылке, указанной в вопросе), а затем я создал get_gs/0 Функция внутри этого модуля только для извлечения GameState следующим образом (server это псевдоним, который я использовал для регистрации PID):

get_gs()->
    server ! {get_gs, self()},
     receive
        GameState ->
            GameState
    end.

А потом внутри loop() функция у меня есть:

{ get_gs, From } ->
           From ! GameState,

           loop(FirstPlayer, SecondPlayer, CurrentPlayer, GameState)

Затем создал модуль, реализующий архитектуру gen_server, и вызвал функцию в следующем порядке (где ->представляет вызовы функций, такие как A->B означает From A i call B):

My custom hook on ejabberd->gen_server based module->gameclient:get_gs/0->gameserver:get_gs/0->tictactoe:get_gs/0

И я получил текущий GameState.

Спасибо @Nathaniel Waisbrot за ваше ценное предложение.

Согласованность данных в реальном времени между распределенными узлами является сложной проблемой, и вам нужно будет адаптировать свое решение к вашим потребностям. Вы не говорите, какую транзакцию вы используете с mnesia, так что, возможно, это решит ваши проблемы.

Однако вот простое решение, которое может помочь вам подумать о проблеме:

Во-первых, давайте назовем один из ваших узлов master, На главном узле запустите gen_server, который обрабатывает игровое состояние. Теперь любой, кто хочет читать или писать игровое состояние, должен сделать rpc:call/4 на главный узел (если они уже там) в gen_server:call/2, Теперь все взаимодействие с состоянием игры происходит синхронно.

Если вы обновляете игровое состояние не чаще, чем несколько раз в секунду, это решение должно работать довольно хорошо для вас. Если игры независимы, то каждая игра является отдельным gen_server.

Другие вопросы по тегам