|
|
||
3. Программная реализация синхронизации |
||
Сначала кратко поговорим о том, как же оператор await может быть реализован на практике. Фактически на однопроцессорной системе исполнение оператора await аналогично исполнению программы обработки прерывания. В этом случае сигнал прерывания можно рассматривать как оповещение о том, что логическое условие приняло значение "истина", а действие является программой обработки прерывания, которая своей первой командой запрещает прерывания. В мультипроцессорных системах нужно также обеспечить, чтобы контексты программ обработки прерываний не пересекались. Теперь перейдем к частным случаям синхронизации, которые могут быть реализованы и без использования механизма обработки прерываний. Сначала рассмотрим условную синхронизацию, а затем проблему взаимного исключения. Задача условной синхронизации Для постановки ЭТОЙ задачи рассмотрим два потока thread_l И thread_2, которые работают следующим образом. Поток thread l выполняет некоторые действия, а затем ждет наступления события event, после которого выполняет другие действия. В свою очередь поток thread_2 также выполняет некоторые действия, а после их завершения оповещает поток thread l о наступлении события event. Затем поток thread_2 выполняет оставшиеся действия. Такая синхронизация работы потоков и называется задачей условной синхронизации. |
||
| bool event = false; // событие event void thread 1() // поток thread 1 { actions before event(); // действия до наступления события while(!event); // ждем, пока событие не произошло actions after event(); // действия после наступления события } void thread 2() // поток thread 2 { some actions!); // действия, о которых оповещает событие event = true; // отмечаем о наступлении события other actions!); // действия, происходящие после события } |
||
Рассматривая этот код, во-первых, заметим следующее — фактически наступление некоторого события равносильно выполнению некоторого действия. Поэтому событие часто и определяют как действие. В потоке thread_1 это действие обозначается функцией some_actions (). Во-вторых, как видно из приведенного программного кода, для решения задачи условной синхронизации для двух потоков достаточно определить глобальную булеву переменную event, начальное значение которой установить в false. Затем в потоке thread_2 установить значение этой переменной в true после наступления события event. Тогда поток thread i ждет наступления события event посредством оператора while (!event) , который циклически проверяет значение булевой переменной event до тех пор, пока эта переменная не примет значение true. Очевидно, что подобным образом задача условной синхронизации может быть решена и для произвольного количества потоков, ждущих наступления события event. Задача взаимного исключения Теперь рассмотрим задачу взаимного исключения. Чтобы упростить рассуждения, эта задача будет сформулирована только для двух параллельных потоков. Сначала предположим, что два параллельных потока работают с одним и тем же ресурсом, который в этом случае называется разделяемым или совместно используемым ресурсом. Далее считаем, что в каждом потоке программный код, который осуществляет доступ к этому ресурсу, заключен в свою критическую секцию. Тогда задача взаимного исключения для двух потоков может быть сформулирована следующим образом: обеспечить двум потокам взаимоисключающий доступ к некоторому совместно используемому ресурсу. Причем решение этой задачи должно удовлетворять следующим требованиям:
|
||
3 |