КАТЕГОРИИ: Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748) |
Использование критической секции
Критическая секция (Critical Section) – это участок кода, в котором поток получает доступ к ресурсу (переменной), который доступен из других потоков (рис. 3.2). Рис. 3.2. Схема критической секции Критические секции (разделы или секции кода, требующие монопольного доступа к разделяемым данным) удобны для управления доступом к данным. Допустим, программа отслеживает показания времени как часы, минуты и секунды, а каждое из этих значений хранится в отдельной целочисленной переменной. Теперь представим, что значения времени совместно используются двумя потоками. Поток А изменяет значение времени и прерывается потоком Б после обновления часов, но до обновления минут и секунд. Результат: поток Б получает недостоверные показания времени. Создавая критическую секцию, вы передаете потокам объект, который они должны использовать совместно. Любой поток, владеющий объектом критической секции, получает доступ к защищенным данным. Остальные потоки вынуждены ожидать освобождения критической секции, захваченной первым потоком, и только после этого какой-либо из них сможет захватить данную критическую секцию и в свою очередь получить доступ к данным. Доступ к защищенным данным может получить не более одного потока одновременно. Для создания в программе, использующей библиотеку MFC, объекта критической секции необходимо создать экземпляр объекта класса ССritical Section, как это показано ниже:
CCriticalSection criticalSection;
Когда в программе необходимо получить доступ к данным, защищенным критической секцией, вызывается метод Lock() объекта этой критической секции, как показано ниже:
criticalSection.Lock();
Если объект критической секции в данный момент не захвачен другим потоком, функция Lock() передаст этот объект во владение данному потоку. Теперь поток может получить доступ к защищенным данным. Завершив обработку данных, поток должен вызвать метод Unlock() объекта критической секции:
criticalSection.Unlock();
Функция Unlock() освобождает объект критической секции. В результате другой поток сможет его захватить и получить доступ к защищенным данным. Лучшим способом реализации механизма защиты данных является размещение критической секции в том же классе, где объявлены данные. Если это сделать, то основной программе не придется беспокоиться о синхронизации работы потоков — методы этого класса возьмут все на себя. Для ознакомления с объектом критической секции создадим в разработанном приложении Example новый пункт Critical Section в меню Thread. При выборе этого пункта будут запускаться две потоковые функции: записи элементов в массив и считывание элементов из массива. Операции чтения и записи в массив защищены критическими секциями. Для реализации этого выполните следующие действия: 1. С помощью редактора ресурсов добавьте новый пункт Critical Section в меню Thread приложения. Присвойте этому пункту идентификатор ID_CriticalSection. 2. С помощью СlassWizard свяжите команду ID_CriticalSection с функцией обработки сообщения void CExampleView::OnCriticalsection(). Перед тем как добавить новую функцию, убедитесь, что в поле Class Name выбрано значение CExampleView. 3. Щелкните на кнопке Edit Code и введите приведенные ниже операторы в новую функцию OnCriticalsection(). AfxBeginThread(WriteThreadProc,this); AfxBeginThread(ReadThreadProc,this);
В этом фрагменте текста программы последовательно вызываются функции WriteThreadProc() и ReadThreadProc(), каждая из них будет работать в своем собственном потоке. 4. Выполните необходимые действия по созданию класса CCountArray. Добавьте в проект два новых пустых файла CountArray.h и CountArray.срр, пользуясь меню File->New. Выберите вкладку Files, типы файлов C/C++ Header File и C++ Source File. Добавьте в пустой файл CountArray.h следующий текст. #include "afxmt.h"class CCountArray {private: int array[10]; CCriticalSection criticalSection;public: CCountArray() {}; ~CCountArray() {}; void SetArray(int value); void GetArray(int dstArray[10]);};
В начале файла к программе подключается файл заголовка библиотеки MFC afxmt.h, обеспечивающий доступ к классу CCriticalSection. В объявлении класса CCountArray выполняется объявление целочисленного массива из десяти элементов, предназначенного для хранения защищаемых критической секцией данных, а также объявляется объект критической секции criticalSection. Открытые методы класса CCountArray включают обыкновенный конструктор и деструктор, а также две функции для чтения и записи массива. Именно два последних метода класса и должны работать с объектом критической секции, так как только они имеют доступ к массиву. 5. Добавьте в пустой файл CountArray.cpp следующий текст.
#include "stdafx.h" #include "CountArray.h" void CCountArray::SetArray(int value) { criticalSection.Lock(); for (int x=0; x<10; ++x) array[x] = value; criticalSection.Unlock(); } void CCountArray::GetArray(int dstArray[10]) { criticalSection.Lock(); for (int x=0; x<10; ++x) dstArray[x] = array[x]; criticalSection.Unlock(); }
Каждый метод этого класса обеспечивает захват и освобождение объекта критической секции. Это означает, что любой поток может вызвать эти методы, абсолютно не заботясь о синхронизации потоков. Например, если поток номер один вызовет функцию SetArray(), первое, что сделает эта функция, будет вызов criticalSection.Lock(), которая передаст объект критической секции во владение этому потоку. Затем весь цикл for выполняется в полной уверенности, что его работа не будет прервана другим потоком. Если в это время поток номер два вызовет функцию SetArray() или GetArray(), то очередной вызов criticalSection.Lock() приостановит работу потока до тех пор, пока поток номер один не освободит объект критической секции. А это произойдет тогда, когда функция SetArray() закончит выполнение цикла for и вызовет criticalSection.Unlock(). Затем система возобновит работу потока номер два, передав ему во владение объект критической секции. 6. Откройте файл CExampleView.cpp и добавьте в него приведенную ниже строку, поместив сразу после строки #include "afxmt.h":
#include "CountArray.h"
7. Добавьте приведенную ниже строку в начало этого же файла, сразу после строки volatile bool keeprunning;
CCountArray сountArray;
8. Добавьте в файл ExampleView.cpp перед функцией void CExampleView:: OnCriticalsection() функции: UINT WriteThreadProc(LPVOID param) { for(int x=0; x<10; ++x) { countArray.SetArray(x); Sleep(1000); } return 0; } UINT ReadThreadProc(LPVOID param) { int array[10]; for (int x=0; x<20; ++x) { countArray.GetArray(array); char str[50]; str[0] = 0; for (int i=0; i<10; ++i) { int len = strlen(str); sprintf(&str[len], "%d ", array[i]); } AfxMessageBox(str); } return 0; }
Откомпилируйте новую версию приложения Example и запустите ее на выполнение. На экране раскроется главное окно приложения. Для запуска процесса выберите команду Thread->Critical section. Первым появится окно сообщений, отображающее текущие значения элементов защищенного массива. Каждый раз при закрытии оно будет появляться вновь, отображая обновленное содержимое массива. Всего вывод окна будет повторяться 20 раз. Значения, отображаемые в окне сообщений, будут зависеть от того, насколько быстро вы будете закрывать это окно сообщений. Первый поток записывает новые значения в массив ежесекундно, причем даже тогда, когда вы просматриваете содержимое массива с помощью второго потока. Обратите внимание на одну важную деталь: второй поток ни разу не прервал работу первого потока во время изменения им значений в массиве. На это указывает идентичность всех десяти значений элементов массива. Если бы работа первого потока прерывалась во время модификации массива, то десять значений массива были бы неодинаковы. Если вы внимательно проанализируете исходный текст программы, то увидите, что первый поток с именем WriteThreadProc() вызывает функцию-член SetArray() класса CCountArray десять раз за один цикл for. В каждом цикле функция SetArray() захватывает объект критической секции, заменяет содержимое массива переданным ей числом и вновь освобождает объект критической секции. Второй поток ReadThreadProc() также пытается захватить объект критической секции, чтобы иметь возможность сформировать строку на экране, содержащую текущие значения элементов массива. Но если в данный момент поток WriteThreadProc() заполняет массив новыми значениями, поток ReadThreadProc() вынужден будет ждать. И наоборот, поток WriteThreadProc() не сможет получить доступ к защищенным данным до тех пор, пока поток ReadThreadProc() не освободит объект критической секции. Если вы хотите убедиться в том, что объект критической секции работает именно так, как описано выше, удалите строку criticalSection.Unlock(), расположенную в конце метода SetArray() класса CCountArray. Затем откомпилируйте и выполните программу. На этот раз после запуска потоков вы не увидите никаких сообщений. Поток WriteThreadProc() захватывает объект критической секции и не освобождает его, что заставляет систему остановить работу потока ReadThreadProc() раз и навсегда (или по крайней мере до окончания работы программы).
Дата добавления: 2014-11-29; Просмотров: 791; Нарушение авторских прав?; Мы поможем в написании вашей работы! Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет |