KNOW INTUIT, Лекция, Потоци
критични участъци. Блокиране. изявление за заключване
Критичният раздел от кода на едно многонишково приложение е фрагментът от код, в който се работи с общ ресурс от нишки. За да се реши проблемът с „надпреварата с данни“, достатъчно е само една нишка да може да работи в критичната секция на кода в даден момент, други нишки трябва да стоят на опашка, чакайки нишката, изпълняваща критичната секция, да завърши работата си. След това друга нишка ще може да започне да изпълнява критичната секция. Тази схема на работа се нарича "блокиране" на нишки. Докато една нишка работи в критичната секция, всички останали нишки, претендиращи за споделения ресурс, са блокирани. Има различни заключващи механизми.
Нека разгледаме най-простия механизъм за заключване, базиран на използването на езиковия оператор C# - операторът за заключване. Нека нашето приложение има няколко критични секции, които използват един и същ ресурс. Различни теми могат да влизат в различни раздели. Трябва обаче да блокирате всички, с изключение на този, в който работи активната нишка, която вече е успяла да заграби ресурса. Решението на проблема е да се създаде някакъв обект, видим във всички критични секции. Това обикновено е обект от общ тип обект, наречен например locker. След това всяка критична секция се затваря чрез команда за заключване с шкафчето за ключове. Синтактично конструкцията на ключалката изглежда така:
Семантиката на структурата е следната. Ще приемем, че обектът шкафче може да бъде в две състояния – „отворено” или „затворено”. В изходно състояние обектът е отворен. Когато нишката достигне критична секция с ключов обект на шкафче, тогава, ако обектът е отворен, нишката започва да изпълнява критичната секция, следпревключване на обекта на шкафчето в "затворено" състояние. Когато критичната секция приключи, обектът на шкафчето се прехвърля в "отворено" състояние. Когато дадена нишка достигне критичната секция със затворен обект на шкафче, тя се поставя на опашка за изпълнение на критичната секция. Веднага щом шкафчето се отвори, първата нишка на свой ред влиза в критичната секция, като заключва ресурса за други нишки в опашката.
Този начин на блокиране ви позволява да се справите с проблема с "надпреварата с данни" и да напишете нишково безопасно приложение за работа с банкови сметки.
Сигурна работа с банкови сметки
След като прочетоха горния текст, програмистите на Sequential и Parallelnov осъзнаха грешката си и бързо направиха необходимите промени в проекта си. Преди да прочетете тяхното решение, опитайте да отговорите на въпроса как да промените съществуващо решение:
- Направете промени в класа Account;
- Създайте нов клас Account_new ;
- Промяна на логиката на работа на клиентите с банкова сметка;
- Друго решение на проблема.
Първият вариант нарушава един от основните принципи на ООП - никога не правете промени във вече работеща класа, когато възникнат нови нужди. Класът Account върши работата си добре при последователно програмиране, когато няма нишки и паралелни изчисления.
Създаването на нов клас е непрактично, тъй като съществуващият клас има много полезни неща и не искате да повтаряте вече свършената работа. Това отново нарушава принципите на ООП.
Логиката на работа с клиента не трябва да се променя. Състезанието за данни възниква в критични секции, свързани с методите на класа Account.
Правилното решение на проблема е да се създаде клас Safe_Account, който наследява класа Account. В нашатаВ задачата споделеният ресурс е обект на акаунт, който представлява банкова сметка, споделена от множество клиенти. Критичните секции се срещат във всички методи на класа, които променят стойностите на полетата на обекта. Тези методи изискват блокиране и съответно предефиниране в класа наследник. Ето как изглежда класът Safe_Account:
Класът въвежда статичен обект Locker, който се използва за заключване на критични секции, представени от методите Add и Sub - попълване и теглене на пари от сметката.
Когато работите с клиенти, вече е необходимо да използвате обект от класа Safe_Account:
Сега работата със семейния акаунт се извършва правилно. Ето как изглеждат резултатите от сесията след актуализацията:
Както виждате, сега всичко е наред. Надпреварата за данни е елиминирана. Парите са изтеглени точно толкова, колкото трябва.