Как избежать состояния гонки - PullRequest
2 голосов
/ 06 декабря 2010

Следующий фрагмент кода взят на странице 112 книги Франческо Чезарини и Саймона Томпсона, «Программирование на Эрланге», как иллюстрация возможного состояния гонки в Эрланге.

start() -> 
  case whereis(db_server) of
    undefined -> 
      Pid = spawn(db_server, init, []), 
      register(db_server, Pid), 
      {ok, Pid};
    Pid when is_pid(Pid) ->
      {error, already_started}
  end.

Не копируя дословно подробности, авторы объясняют, что если два процесса одновременно выполняют start (), то процесс 1, выполняющий секцию «undefined», может не завершиться, поскольку процесс 2 приводит к его прерыванию. Процесс 2 затем запустит раздел «неопределенный» до завершения. Теперь, когда процесс 1 возобновляется, db_server уже был зарегистрирован процессом 2, в результате чего его вызов register () выдает ошибку времени выполнения. Надеюсь, вы понимаете, о чем я, потому что я не хочу вставлять текст книги.

Мой вопрос: как можно кодировать вышеуказанную точную функциональность, чтобы избежать возможного состояния гонки, когда два процесса одновременно выполняют start ()?

Ответы [ 3 ]

9 голосов
/ 06 декабря 2010

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

start() ->
   Pid = spawn(db_server, init, [self()]),
   receive {Pid, StartResult} ->
       StartResult
   end.

init(Parent) ->
    try register(db_server, self()) of
        true ->
            Parent ! {ok, started},
            real_init()
    catch error:_ ->
        Parent ! {error, already_started}
    end.

(Может не скомпилироваться или работать. Печатается здесь без проверки.:))

Тщательно реализованную версию этого можно найти в gen.erl. На практике в реальном коде вы просто используете поведение OTP для повторного использования этой версии.

2 голосов
/ 06 декабря 2010

Для сериализации запросов можно использовать gen_server .

1 голос
/ 07 декабря 2010

Сколько серверов вы хотите запустить? Ваш первоначальный вопрос подразумевает один, а в комментарии к @cthulahoops - два, сервер и резервная копия. Для двух серверов вы можете попробовать что-то вроде:

start() ->
    case whereis(db_server) of
        undefined ->
            Spid = spawn(db_server, init, []),
            %% In race condition there can be only one that succeeds to register
            case catch register(db_server, Spid) of
                true -> {ok,Spid};             %We are it
                {error,_} ->                   %Server registered, register as backup
                    register(db_server_backup, Spid),
                    {ok,Spid}
            end;
        _ ->                                   %Server registered, start backup
            Bpid = spawn(db_server, init, []),
            register(db_server_backup, Bpid),
            {ok,Bpid}
    end.

Я не запускал его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...