Ответ 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 возвращался до обработки запроса. (Если запрос не поставлен в очередь, но в этом случае результат по-прежнему определяется, поскольку мы точно знаем, как и когда очередь читается.)
Заключение
Короче говоря, однопоточные программы не освобождают от триггеров условий гонки. Но они могут встречаться только с другими потоками внешних программ или между ними. Результат нашей программы может быть неопределенным, потому что вход, который наша программа получает от этих других программ, не определен.