Читайте также: |
|
}
Здесь предполагается, что функции обоих потоков дают одинаковый результат, хотя они и закодированы с небольшими различиями. Если бы исполнялась только функция FirstThread, она заполнила бы массив g_dwTimes набором чисел с возрастающими значениями. Это верно и в отношении функции SecondThread - еcли бы она также исполнялась независимо. В идеале обе функции, даже выполняясь одновременно, должны бы по-прежнему заполнять массив тем же набором чисел. Но массив g_dwTimes не будет заполнен как надо, так как функции обоих потоков одновременно обращаются к одним и тем же глобальным переменным. Допустим, началось исполнение обоих потоков в системе с одним процессом. Первым включается в работу второй поток, т.е. функция SecondThread (что вполне вероятно), и только она успела увеличить g_nIndex до 1, как система вытеснила ее поток и перешла к исполнению FirstThread. Та заносит в g_dwTimes [1] показания системного времени, и процессор вновь переключается на исполнение второго потока. SecondThread теперь присваивает элементу g_dwTimes [1-1] новые показания системного времени. Поскольку эта операция выполняется позже, новые показания, естественно, выше, чем помещенные в элемент g_dwTimes[1] функцией FirstThread, при этом сначала заполняется первый элемент массива и только потом нулевой. Таким образом, данные в массиве оказываются ошибочными.
Хотя приведенный пример довольно искусственный, важно другое: теперь можно легко представить, что может произойти в действительности. Например, при управлении связанным списком объектов,. если доступ к связанному списку не синхронизирован, один поток может добавить элемент в список в тот момент, когда другой поток пытается найти в нем определенный элемент. Еще более непредсказуемые последствия могут быть, если оба потока одновременно добавят в список новые элементы. Так что, используя критические разделы, можно и нужно скоординировать доступ потоков к структурам данных.
1.2. Пример приложения, использующего критические разделы
Приложение CritSecs (CRITSECS.EXE) - его листинг см. в Приложении 1- демонстрирует, как важно применять критические разделы в многопоточных приложениях. После запуска программы функция WinMain активизирует специальное диалоговое окно, служащее интерфейсом этого приложения. Когда функция диалогового окна получает сообщение WM_INITDIALOG, функция Dlg_OnInitDialog инициализирует глобальную структуру CRITICAL_SECTION и все элементы управления в диалоговом окне, а также создает два потока CounterThread и DisplayThread. С этого момента в процессе начинают исполняться три потока: первичный (обрабатывающий данные, вводимые в диалоговое окно и его элементы управления), CounterThread и DisplayThread.
Где-то в начале программы CRITSECS.С появляется следующая переменная:
//Данные, которые подлежат защите
TCHAR g_szNumber [10]=__TEXT (‘0’);
Это символьный массив, инициализируемый строкой, содержащей цифру 0. Поток CounterThread преобразует ее в целое число, прибавляет 1 и, преобразовав в строку, вновь заносит в символьный массив. Поток DisplayThread считывает это число из массива g_szNumber и добавляет его в окно списка, расположенное в диалоговом окне.
После запуска программы CritSecs список заполняется числами, и через некоторое время диалоговое окно Critical Section Test Application
становится таким, как представлено на рис. 1.
Рис.1. Диалоговое окно, демонстрирующее работу приложения CritSecs
После запуска программы числа в списке не располагаются по возрастанию, потому что по умолчанию программа не синхронизирует доступ потоков к массиву g_szNumber. В то время как поток CounterThread преобразует элемент массива в число, увеличивает его значение и копирует обратно в массив, поток DisplayThread читает содержимое массива g_szNumber и копирует его в окно списка.
Если теперь активизировать флаг Synchronize (Синхронизировать), то приложение сразу приступает к использованию переменной g_CriticalSection, позволяющей ограничить доступ к массиву g_szNumber. В итоге числа выводятся на экран строго по возрастающей.
Комбинированные списки Process Priority Class, Display Thread Priority и Counter Thread Priority дают возможность варьировать класс приоритета приложения CritSecs, равно как и относительные приоритеты двух потоков, исполняющих функции CounterThread и DisplayThread.
Флаг Pause (Пауза) демонстрирует, как приостанавливается и возобновляется выполнение потока CounterThread и DisplayThread.
Флаг Show Counter Thread (Показать поток Counter Thread) заставляет функцию Counter Thread по окончании каждой итерации выводить в список следующую строку:
Cntr: Increment
При активизации этого флага каждая итерация цикла в DisplayThread быстрее, чем итерация каждого цикла в CounterThread. Иногда DisplayThread за одну итерацию цикла в CounterThread успевает выполнить две итерации своего цикла. Это лишний раз доказывает, как важно предвидеть поведение операционной системы при распределении процессорного времени между потоками. Варьируя относительные приоритеты двух потоков, можно изменить порядок и частоту выделения им квантов времени.
Приложение CritSecs может дать разные результаты в зависимости от определенных обстоятельств и характеристик компьютера:
количества процессоров в системе (при работе в Windows NT);
производительности компьютера;
количества потоков, созданных другими, одновременно выполняемыми процессами;
класса приоритета прочих выполняемых процессов;
относительного приоритета потоков, выполняемых в этих процессах.
Дата добавления: 2015-07-11; просмотров: 53 | Нарушение авторских прав