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

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

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

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

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

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

    Короткий:

    1. Состояние гонки может происходить только между двумя или более streamами. Мы не можем иметь условия гонки внутри одного процесса streamа (например, в одном streamе, без выполнения ввода / вывода).

    2. Но одна программа streamа может во многих случаях:

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

      2. инициировать условие гонки между или с другими streamами (нитями), например:

        1. другие программы, такие как клиенты
        2. streamи библиотек или серверы

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

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

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

    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) Условие гонки между другими streamами, например:

    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); } ); 

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

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

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

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

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

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

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

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

    Пример :

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

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

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

    Вывод

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

    Давайте будем гением компьютера.