Я думаю, что, является ли приложение многопоточным или многопроцессорным, или что-то еще, на самом деле не является проблемой, которую вам нужно решить. Реальная проблема заключается в следующем: как сделать эту операцию атомарной?
Когда вы говорите, что вам нужно убедиться, что шаг 3 всегда происходит, вы на самом деле говорите, что ваша программа должна выполнить атомарную транзакцию: либо все это происходит, либо ничего не происходит.
Чтобы достичь этого, ваш процесс должен быть спроектирован так, как планируются транзакции базы данных. Он должен начать транзакцию, выполнить работу, а затем либо зафиксировать транзакцию, либо откатить ее. Процесс должен быть спроектирован таким образом, чтобы, если при запуске он обнаружил, что транзакция была начата, а не зафиксирована или откатана более ранним запуском, он должен начать с отката этой транзакции.
Важно отметить, что метод commit должен иметь как можно меньшую нестабильность. Например, типичным способом разработки транзакционного процесса является использование файловой системы: создайте временный файл, чтобы указать, что транзакция началась, запишите выходные данные во временные файлы, а затем попросите метод commit переименовать временные файлы в их окончательные. имена и удалить файл флага транзакции. Существует риск того, что файловая система выйдет из строя между временем, когда вы переименовали файлы, и временем, когда вы удалили файл флажка (и есть способы уменьшить этот риск тоже), но это гораздо меньший риск, чем вид, который вы описали.
Если вы разрабатываете процесс так, чтобы он реализовывал транзакционную модель, будь то многопроцессорная обработка, многопоточность или однопоточность, это просто детали реализации.