Как «модель актора» решает проблему «разделяемого состояния»?

Похоже, что porg «снять» — это классический пример, он используется в «sicp» и «концепциях проектирования в языках программирования» для объяснения «общего состояния».

Я хочу знать, есть ли в «модели актера» какой-то способ избежать «общего состояния»? Но я не могу найти хороший пример записи в erlang/elixir, чтобы показать это.

Пример изъятия есть в 《programming erlang》2ed, глава 22, но пример, кажется, показывает, как написать opt, а не как обращаться с «общим состоянием»: он использует базу данных ets для сохранения «баланса», поэтому ets - это «общее состояние», и он использует только один процесс, а не два для «снятия» и «депозита».

Итак, есть ли хороший пример «снятия», чтобы показать, как erlang/elixir справляется с проблемой «общего состояния»? Я думаю, что для его обработки нужно кодировать баланс в сообщении и передавать «баланс» везде, чтобы избежать поделитесь им в месте исправления. Возможно, MVar haskell решит это

Почтовый ящик Process решил бы эту проблему из коробки, если бы я правильно понял проблему.

Aleksei Matiushkin 16.02.2023 09:09

Снять "порг"? Серьезно, это опечатка или я что-то пропустил?

Onorio Catenacci 22.02.2023 15:39
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Веб-скрейпинг, как мы все знаем, это дисциплина, которая развивается с течением времени. Появляются все более сложные средства борьбы с ботами, а...
Калькулятор CGPA 12 для семестра
Калькулятор CGPA 12 для семестра
Чтобы запустить этот код и рассчитать CGPA, необходимо сохранить код как HTML-файл, а затем открыть его в веб-браузере. Для этого выполните следующие...
ONLBest Online HTML CSS JAVASCRIPT Training In INDIA 2023
ONLBest Online HTML CSS JAVASCRIPT Training In INDIA 2023
О тренинге HTML JavaScript :HTML (язык гипертекстовой разметки) и CSS (каскадные таблицы стилей) - две основные технологии для создания веб-страниц....
Как собрать/развернуть часть вашего приложения Angular
Как собрать/развернуть часть вашего приложения Angular
Вам когда-нибудь требовалось собрать/развернуть только часть вашего приложения Angular или, возможно, скрыть некоторые маршруты в определенных средах?
Запуск PHP на IIS без использования программы установки веб-платформы
Запуск PHP на IIS без использования программы установки веб-платформы
Установщик веб-платформы, предлагаемый компанией Microsoft, перестанет работать 31 декабря 2022 года. Его закрытие привело к тому, что мы не можем...
Оптимизация React Context шаг за шагом в 4 примерах
Оптимизация React Context шаг за шагом в 4 примерах
При использовании компонентов React в сочетании с Context вы можете оптимизировать рендеринг, обернув ваш компонент React в React.memo сразу после...
1
2
59
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Актер или процесс Erlang/Elixir фактически является одним потоком. Если вы находитесь в функции handle_call GenServer, вы гарантированно не получите другое сообщение или не вызовете другое handle_call, пока этот конкретный обработчик сообщений не будет завершен. Все сообщения, отправленные процессу, принимаются в определенном порядке и обрабатываются по одному; в процессе нет параллелизма и, следовательно, нет возможности для одновременного изменения состояния.

Минимальная установка Эликсира может выглядеть так:

defmodule Account do
  use Genserver

  def start_link(balance) do
    GenServer.init(__MODULE__, balance)
  end

  def deposit(account, amount) do
    GenServer.call(account, {:deposit, amount})
  end

  def withdraw(account, amount) do
    GenServer.call(account, {:withdraw, amount})
  end

  @impl true
  def init(balance) do
    {:ok, balance}
  end

  @impl true
  def handle_call({:deposit, amount}, _, balance) do
    new_balance = balance + amount
    {:reply, :ok, new_balance}
  end

  @impl true
  def handle_call({:withdraw, amount}, _, balance) do
    if amount < balance do
      {:reply, {:error, :insufficient_balance}, balance}
    else
      new_balance = amount - balance
      {:reply, :ok, new_balance}
    end
  end
end

В классической многопоточной среде с изменяемым состоянием у вас есть возможность для одного потока вычислить new_balance, в то время как другой поток перезаписывает существующий баланс, и изменения могут быть потеряны. (Вы цитируете «Структура и интерпретация компьютерных программ», и здесь есть целый подраздел, описывающий проблемы.) Но поскольку актор является однопоточным, даже если несколько других процессов вызывают Account.withdraw/2 для одной и той же учетной записи, вы гарантированно получите непротиворечивое поведение.

Просто добавим к тому, что объяснил Дэвид Мейз: процесс отправляет сообщение генератору OTP, вызывая функцию:

gen_server:call(GenserverModuleName, Message)

Когда процесс A вызывает эту функцию, процессу genserver отправляется сообщение, например, в главе 22 сообщение может быть отзывом: {remove, "account0001", 200}. Когда процесс B вызывает эту функцию, процессу genserver отправляется другое сообщение, например. очередной вывод: {remove, "account001", 1000}. Процесс genserver, как и все процессы erlang, имеет почтовый ящик, в котором накапливаются сообщения от всех процессов, отправляющих ему сообщения.

Генсервер затем ищет в почтовом ящике сообщения, которые он знает, как обрабатывать, например. сообщения, соответствующие параметрам, указанным в различных пунктах определения функции handle_call(). Однако генсервер работает только с одним сообщением за раз, поэтому не может быть состояния гонки, т. е. когда два процесса пытаются одновременно изменить один и тот же фрагмент данных, например баланс учетной записи. Генсервер обработает одно сообщение о выводе средств, и если на счете достаточно большой баланс, то вывод будет разрешен, а баланс будет обновлен в таблице ets. Затем genserver обработает следующее сообщение о выводе средств, и, если новый баланс достаточно велик, будет разрешен второй вывод средств, а баланс будет обновлен в таблице ets. Другими словами, генератор-сервер не выделяет два процесса для одновременной обработки двух сообщений о снятии средств, а последовательно обрабатывает два сообщения о снятии средств.

он использует базу данных ets для сохранения «баланса», поэтому ets является «общим состояние

Genserver — единственный процесс, который знает о таблице ets, и genserver обращается к таблице ets только последовательно.

Я думаю, что он должен закодировать баланс в сообщении, чтобы обработать его, и передавайте «баланс» везде, чтобы не делиться им в фиксированном месте.

Нет, баланс может оставаться в таблице ets по указанным выше причинам.

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