Слишком часто, наши представления сформированы извне, без понимания сути... Захотелось понять, откуда взялось понятие "асинхронного программирования". Материалов в И-нете оказалось подозрительно много, а когда я добрался до ссылок на материалы от Майкрософта... картина прояснилась. Сразу вспомнилось... Младшая дочь лет 10 назад сертифицировалась (модно это тогда было) по MSSQL. Попался ей вопрос о наличии вложенных транзакций в MSSQL. Она ответа не знала и спросила меня. Я ей, конечно, сказал, что никаких вложенных транзакций в MSSQL сервере нет и быть не может. Оказалось, что я "ошибся", оценку снизили... Пришлось поднять для дочери работы Эллиота Мосса, который собственно, ввел это понятие "вложенные транзакции", наложить их на архитектуру MSSQL... В Майкрософте свою ошибку "со скрипом" признали, но, конечно... менять не стали ни оценку, ни учебные материалы.
Примерно тоже самое творится вокруг "асинхронного программирования"... только непонятно, кто этот термин ввел в оборот... и зачем... В свое время пришлось довольно долго объяснять, что даже термин "асинхронный вызов" не является корректным. Асинхронность касается только возврата, но не может относится к вызову. Следовательно, и понятия "параллельные нити, потоки, процессы" тоже не являются корректными. Конечно, можно пользоваться этими понятиями, если удобно, но не следует забывать, что это всего лишь... "упрощения".
Рассмотрим простой пример:
Код:
A := fun1;
B := fun2;
C := A * B;
Данной задаче совершенно безразлично, в каком порядке вызываются функции fun1 и fun2, главное, чтобы к моменту вычисления значения переменной C, переменные A и B были вычислены. Поэтому нет никакой разницы, какой из первых двух операторов выполняется первым, мало того, они оба вполне могут выполняться параллельно. Результат вычисления C не изменится от порядка вычисления A и B. Поэтому точка вычисления C является точкой синхронизации процессов вычисления A и B.
Ситуация становится не такой однозначной в том случае, если появляется понятие контекста исполнения. Например, пусть в контексте исполнения есть переменная D:
Код:
function fun1: Integer;
begin
...
D := D + 256;
...
end;
function fun2: Integer;
begin
...
Result := D * 2;
...
end;
Теперь порядок исполнения имеет значение. Если исходное состояние D = 1, то при выполнении в порядке: fun1, fun2 - результат fun2 будет равен 514, при порядке: fun2, fun1 - результат fun2, будет равен 2. Соответственно, изменится и результат вычисления переменной C. В таких случаях (изменения контекста), должен строго соблюдаться порядок исполнения. (Примечание: контекст не всегда можно выделить явно, в виде набора переменных, он может быть неявным, то есть, определяться внутренними функциональными зависимостями между элементами... системы/программы).
Из сказанного следует довольно банальный вывод. Необходимо хорошо продумывать структуру программы (здравствуйте Н. Вирт) и/или архитектуру системы, не допуская ненужных/избыточных зависимостей, а необходимые зависимости должны быть оформлены, как состояния. Изменение состояния программы/системы или их частей должны быть локализованы в коде и (тщательно) обоснованы/документированы. В случае одновременного использования одной сущности (shared entity) необходимо рассматривать способы определения и разрешения конфликтных ситуаций. Эти способы общеизвестны.
А "асинхронное программирование" - это в значительной части... затуманивание мозгов, IMHO.