Если 3 клиента одновременно вызывают функцию, функция будет увеличивать счетчик только один раз, и я получу ошибку. Как заставить функцию ждать, пока другой закончит, а затем выполнит?
Я бы использовал простой счетчик, построенный с помощью gen_server
, и только получить доступ к счетчику на нем. Использование gen_server
, как это, гарантирует, что вы никогда не получите условие гонки доступа к файлу.
Вот что-то, чтобы вы начали:
-module(file_counter).
-export([start_link/0, start/0, increment/0]).
-behaviour(gen_server).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
start() ->
gen_server:start({local, ?MODULE}, ?MODULE, [], []).
increment() ->
gen_server:call(?MODULE, increment).
init([]) ->
{ok, "file_counter.txt"}.
handle_call(increment, _From, File) ->
Counter = case file:read_file(File) of
{ok, Binary} -> binary_to_integer(Binary);
{error, enoent} -> 0
end,
ok = file:write_file(File, integer_to_binary(Counter + 1)),
{reply, Counter, File}.
handle_cast(_Req, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
Демо:
1> c(file_counter).
{ok,file_counter}
2> file_counter:start_link().
{ok,<0.40.0>}
3> file_counter:increment().
0
4> file_counter:increment().
1
5> file_counter:increment().
2
6> [ spawn_link(file_counter, increment, []) || _ <- lists:seq(1, 9998) ].
[<0.45.0>,<0.46.0>,<0.47.0>,<0.48.0>,<0.49.0>,<0.50.0>,
<0.51.0>,<0.52.0>,<0.53.0>,<0.54.0>,<0.55.0>,<0.56.0>,
<0.57.0>,<0.58.0>,<0.59.0>,<0.60.0>,<0.61.0>,<0.62.0>,
<0.63.0>,<0.64.0>,<0.65.0>,<0.66.0>,<0.67.0>,<0.68.0>,
<0.69.0>,<0.70.0>,<0.71.0>,<0.72.0>,<0.73.0>|...]
7> file_counter:increment().
10001
Просто позвоните file_counter:increment()
, прежде чем принимать каждый вызов и использовать возвращаемое ей значение как кол.
Редактировать: Это просто быстрый модуль, который я написал. Вероятно, вы должны изменить имя файла, передав его start
, start_link
и init
, а также не зарегистрировать процесс с именем, если хотите иметь возможность запуска нескольких копий счетчика. Код здесь действительно POC, чтобы вы начали.
(В системе с SSD-диском, я был в состоянии выполнить file_counter:increment()
около 5000 раз в секунду.)
Более эффективным будет читать счетчик при запуске и хранить его в ETS (или с сохранением состояния процесса) , а затем запишите его при выключении. Однако: это не на 100% надежнее. Что произойдет, если вы «забудете» о количестве вызовов и попробуйте позвонить с более ранним номером? –
Веб-служба вернет некоторую ошибку, и следующие вызовы не имеют смысла. Вот почему так важно держать счетчик в порядке. –
@Roger Lipscombe –