Можем ли мы иметь условия гонки в однопоточной программе?

Вы можете найти на здесь очень хорошее объяснение того, что такое состояние гонки.

Недавно я видел много людей, которые сбивали с толку заявления о гоночных условиях и потоках.

Я узнал, что условия гонки могут возникать только между потоками. Но я видел код, похожий на условия гонки, на языке событий и асинхронных языков, даже если программа была одиночной нитью, например, в Node.js, в GTK + и т.д.

Можем ли мы иметь условие гонки в одной программе потока?

Ответ 1

Все примеры находятся на вымышленном языке, очень близком к Javascript.

Короткие:

  • Состояние гонки может встречаться между двумя или более потоками. Мы не можем иметь условия гонки внутри одного процесса потока (например, в одном потоке, не выполняющем программы ввода-вывода).

  • Но во многих случаях может использоваться одна программа потока:

    • дают ситуации, похожие на условия гонки, например, в программе на основе событий с циклом событий, но не являются реальными условиями гонки

    • вызывает состояние гонки между или с другими потоками, например:

      • другие программы, такие как клиенты
      • потоки или серверные библиотеки

I) Условия гонки могут возникать только между двумя или более потоками

Условие гонки может возникать только тогда, когда два или более потока пытаются получить доступ к совместно используемому ресурсу, не зная, что он модифицируется в то же время неизвестными инструкциями из другого потока (-ов). Это дает неопределенный результат. (Это действительно важно.)

Процесс с одним потоком - это не что иное, как последовательность известных инструкций, поэтому результат определяется, даже если порядок выполнения инструкций нелегко читать в коде.

II) Но мы небезопасны

II.1) Ситуации, похожие на условия гонки

Многие языки программирования реализуют функции асинхронного программирования через события или сигналы, обрабатываемые основным циклом или циклом событий, которые проверяют очередь событий и запускают прослушиватели. Пример этого - Javascript, libuevent, reactPHP, GNOME GLib... Иногда мы можем найти ситуации, которые, как представляется, являются условиями гонки, , но они не.

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

Пример:

setTimeout(
  function() { console.log("EVENT LOOP CALLED"); },
  1
); // We want to print EVENT LOOP CALLED after 1 milliseconds

var now = new Date();
while(new Date() - now < 10) //We do something during 10 milliseconds

console.log("EVENT LOOP NOT CALLED");

в выводе Javascript всегда (вы можете протестировать в node.js):

EVENT LOOP NOT CALLED
EVENT LOOP CALLED

потому что цикл события вызывается, когда стек пуст (все функции возвращены).

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

II.2) Условие гонки между другими потоками, например:

II.2.i) С другими программами, такими как клиенты

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

Пример:

var step;
on('requestOpen')(
  function() {
    step = 0;
  }
);

on('requestData')(
  function() {
    step = step + 1;
  }
);

on('requestEnd')(
  function() {
    step = step +1; //step should be 2 after that
    sendResponse(step);
  }
);

Здесь у нас есть классическая установка условий гонки. Если запрос открывается перед другим концом, step будет reset до 0. Если два события requestData запускаются до requestEnd из-за двух одновременных запросов, шаг будет достигнут 3. Но это происходит потому, что мы принять последовательность событий как неопределенное. Мы ожидаем, что результат программы в большинстве случаев не определен с неопределенным вводом.

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

Существует два способа понять вещь:

  • Мы можем рассматривать клиентов как часть нашей программы (почему бы и нет?), и в этом случае наша программа является многопоточным. Конец истории.
  • Чаще всего мы можем считать, что клиенты не являются частью нашей программы. В этом случае они просто ввод. И когда мы рассматриваем, имеет ли программа определенный результат или нет, мы делаем это с введенным значением. В противном случае даже простейшая программа return input; имела бы неопределенный результат.

Обратите внимание, что:

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

II.2.ii) С помощью библиотеки (-ов)

В наших программах мы часто используем библиотеки, которые порождают другие процессы или потоки, или просто делают I/O с другими процессами (и I/O всегда неопределен).

Пример:

databaseClient.sendRequest('add Me to the database');

databaseClient.sendRequest('remove Me from the database');

Это может вызвать условие гонки в асинхронной библиотеке. Это так, если sendRequest() возвращается после отправки запроса в базу данных, но до того, как запрос действительно выполнен. Мы немедленно отправляем другой запрос, и мы не можем знать, будет ли первое выполнено до того, как будет оценено второе, потому что база данных работает в другом потоке. Существует условие гонки между программой и процессом базы данных.

Но, если база данных находилась в том же потоке, что и программа (которая в реальной жизни не часто случается), было бы невозможно, чтобы sendRequest возвращался до обработки запроса. (Если запрос не поставлен в очередь, но в этом случае результат по-прежнему определяется, поскольку мы точно знаем, как и когда очередь читается.)

Заключение

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

Ответ 2

Условия гонки могут возникать в любой системе, в которой одновременно выполняются процессы, которые создают изменения состояния во внешних процессах, например:

  • многопоточность,
  • циклы событий,
  • многопроцессорность,
  • параллелизм на уровне команд, когда выполнение инструкций должно выполняться не по порядку, чтобы избежать условий гонки,
  • схемотехника,
  • знакомства (романтика),
  • реальные гонки, например Олимпийские игры.