Синхронизация в многопоточни MFC приложения - всичко за ИТ и програмиране
Написано на 18 май 2011 г.
СЪДЪРЖАНИЕ
Обикновено събитията се използват в случаите, когато нишка (или нишки) трябва да започне да се изпълнява след настъпването на определено събитие. Например една нишка може да изчака, докато необходимите данни бъдат събрани и след това да започне да записва данните на твърдия диск. Има два вида събития: ръчно нулиране и автоматично нулиране. Използвайки събитие, можем лесно да кажем на друга нишка, че е извършено определено действие. При първия тип събитие, ръчно нулиране, една нишка може да уведоми повече от една нишка за определено действие. Но с втория тип събитие, а именно събитието за автоматично нулиране, може да бъде уведомена само една нишка. MFC има клас CEvent, който капсулира обект на събитие (представен от стойност HANDLE в условията на Windows). Конструкторът CEvent ни позволява да създаваме ръчно нулиране и автоматично нулиране на събития. По подразбиране се генерира вторият тип събитие. За да уведомим чакащи нишки, трябва да използваме метода CEvent::SetEvent, което означава, че този тип повикване ще накара събитието да премине в сигнализираното състояние. Ако това е събитие за ръчно нулиране, то ще остане в сигнализирано състояние, докато не бъде извикана подходящата процедура CEvent::ResetEvent, която кара събитието да премине в несигнализирано състояние. Това е функция, която позволява на една нишка да уведоми повече от една нишка с едно извикване на функцията SetEvent. Ако събитието е автоматично нулирано, тогава само една нишка от всички чакащи нишки ще може да получи съобщението. След като нишката получи съобщението, събитието автоматично ще премине в състояние без сигнал.Следващите два примера илюстрират тази теория. Първи пример:
Този код създава глобален CEvent обект с автоматично нулиране. В допълнение към това има две работещи нишки, които чакат събитие, което да им каже да започнат да се изпълняват. Веднага щом третата нишка извика SetEvent на този обект, една и само една от двете нишки (обърнете внимание, че никой не казва коя конкретно нишка) ще получи съобщението и след това събитието ще премине в несигнализирано състояние, което ще попречи на втората нишка да улови събитието. Кодът, макар и да не е много полезен, показва как работи събитието за автоматично нулиране. Нека разгледаме втория пример:
Този код се различава от предишния само в параметъра на конструктора CEvent. Но по отношение на функционалността има фундаментална разлика в начина, по който се изпълняват двете нишки. Ако трета нишка извика метода SetEvent на този обект, тогава ще бъде възможно да се гарантира, че двете нишки започват да се изпълняват по едно и също време (почти по едно и също време). Това е така, защото събитие за ръчно нулиране, след като влезе в сигнализирано състояние, няма да премине към несигнализирано състояние, докато съответният метод ResetEvent не приключи изпълнението.
Има и друг метод за работа с тези събития - CEvent::PulseEvent. Този метод първо кара събитието да премине към сигнализирано състояние и след това го кара да се върне обратно в състояние без сигнализиране. Ако събитието се нулира ръчно, когато събитието влезе в сигнализирано състояние, всички чакащи нишки ще бъдат уведомени и след това събитието ще премине в несигнализирано състояние. Ако събитието е автоматично нулирано, тогава само една нишка ще бъде уведомена, дори ако има много чакащи нишки. Ако няма чакащи нишки, тогаваизвикването на ResetEvent няма да направи нищо.