Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
40
Добавлен:
30.03.2015
Размер:
387.58 Кб
Скачать

Многопоточный логический параллелизм

Преимущества «мелкозернистого» логического параллелизма заставляют искать пути снижения накладных расходов, присущих многозадачности. Известные решения для операционных систем — легковесные процессы(light-weight process). Так, в Sun OS 5.x имеется облегченный вариант задач —потоки(thread), состояние которых полностью характеризуется значениями указателей на код и стек. Переключение процессора на поток минимизировано вплоть до операций сохранения/восстановления этих указателей. Планировщик по-прежнему присутствует и активизируется по прерываниям от таймера.

Отметим, что переключение потоков по времени не всегда удобно и приводит к непроизводительным простоям процессора. Если в потоке для продолжения вычислений требуется внешнее событие (отклик оборудования, реакция абонента на запрос, подтверждение успешной передачи сообщения), то поток «честно» тратит отведенное ему время на ожидание:

Step1(); /* действие */ while (!EventOnStep1()); /* ожидание реакции на действие */ Step2(); /* следующее действие */

Если для реакции на действие требуется значительное время (например, событие — это реакция пользователя на запрос системы), то более приемлемая стратегия — передача управления после опроса. Этот подход используется при организации многопоточности методом циклического опроса(round-robin), который иногда обозначают термином «корпоративная многозадачность». При циклическом опросе потоки могут быть представлены просто функциями, которые опрашиваются в бесконечном цикле:

Main(){ for (;;) { thread_1(); thread_2(); ... thread_N(); } }

Необходимость ожидания внешней реакции естественным образом задает разбиение потоков на части, любая из которых является простейшей функцией. Если присвоить каждой части какое-либо число, то реализация потоков естественным образом будет выражаться в терминах языка Си. По историческим причинам простейшие функции называют «состояниями».

thread_i () { switch (State_i) { ... case STATE_1: Step1(); /* действие */ if (EventOnStep1()) State_i = STATE_2; /* смена состояния */ break; case STATE_2: Step2(); /* следующее действие */ ... } }

Таким образом, при многопоточности можно минимизировать или даже полностью исключить накладные расходы, присущие многозадачности. А простота организации делает многопоточность привлекательной для обеспечения «мелкозернистого» логического параллелизма с гранулярностью на уровне нескольких инструкций. Получаемый выигрыш компенсирует риск возможных ошибок (которые могут возникнуть, например, из-за работы с общей областью данных). Мало того, эти риски можно полностью исключить за счет применения соответствующих механизмов.

Например, в языке Рефлекс (диалект языка Си, предназначенный для описания алгоритмов управления) безопасность и невозможность разрушения программы языковыми средствами обеспечены на концептуальном уровне. В этом языке синтаксис и семантика Си расширены за счет понятия процесса — параллельно исполняемого участка кода, допускающего запуск, остановку и проверку своего состояния [4]. Автоматизированы рутинные операции, связанные с организацией параллелизма, и пользователь может полностью сосредоточиться на собственно алгоритме. Возможность обращения по произвольному адресу исключена. Переменные языка типизированы. При таком подходе конфликты памяти попросту невозможны.