{"id":695,"date":"2019-02-11T11:33:58","date_gmt":"2019-02-11T10:33:58","guid":{"rendered":"https:\/\/www.kompikownia.pl\/?p=695"},"modified":"2019-04-17T15:52:44","modified_gmt":"2019-04-17T13:52:44","slug":"programowanie-wspolbiezne-w-jezyku-c-synchronizacja","status":"publish","type":"post","link":"https:\/\/www.kompikownia.pl\/index.php\/2019\/02\/11\/programowanie-wspolbiezne-w-jezyku-c-synchronizacja\/","title":{"rendered":"Programowanie wsp\u00f3\u0142bie\u017cne w j\u0119zyku C++ &#8211; synchronizacja"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Czas czytania:<\/span> <span class=\"rt-time\">5<\/span> <span class=\"rt-label rt-postfix\">minut<\/span><\/span>\n<p class=\"has-text-color has-headings-color\">W poprzedniej cz\u0119\u015bci dowiedzia\u0142e\u015b si\u0119 czym s\u0105 w\u0105tki oraz po co ich u\u017cywa\u0107. Pozna\u0142e\u015b tak\u017ce podstawow\u0105 metod\u0119 synchronizacji, kt\u00f3r\u0105 jest muteks, wprowadzony wraz z wersj\u0105 C++11. Wierz\u0119, \u017ce jest to ogrom przydatnej wiedzy, kt\u00f3r\u0105 z pewno\u015bci\u0105 kiedy\u015b wykorzystasz. Zapoznaj si\u0119 wi\u0119c z kolejn\u0105 cz\u0119\u015bci\u0105 serii kt\u00f3ra sprawi, \u017ce praca z w\u0105tkami w C++ stanie si\u0119 jeszcze przyjemniejsza. B\u0119dziesz pope\u0142nia\u0142 tak\u017ce mniej brzemiennych w skutki b\u0142\u0119d\u00f3w. Gotowy? No to zaczynajmy! <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">W\u0105tki w C++, RAII i Zapominalski mutex.unlock() <\/h2>\n\n\n\n<p class=\"has-text-color has-headings-color\">Wiesz ju\u017c jak dzia\u0142aj\u0105 muteksy. Przypomnijmy sobie pokr\u00f3tce, jak wygl\u0105da\u0142oby tworzenie sekcji krytycznej, do kt\u00f3rej dost\u0119p ma tylko jeden w\u0105tek.<\/p>\n\n\n\n<p class=\"has-text-color has-headings-color\">Musimy mie\u0107 jaki\u015b obiekt klasy mutex, kt\u00f3ry jest wsp\u00f3\u0142dzielony mi\u0119dzy w\u0105tkami. Na pocz\u0105tku sekcji krytycznej u\u017cywamy metody lock() tego mutexa. Na ko\u0144cu &#8222;niebezpiecznego&#8221; obszaru umieszczamy wywo\u0142anie metody unlock obiektu muteksa. <\/p>\n\n\n<div class=\"codecolorer-container cpp default\" style=\"overflow:auto;white-space:nowrap;width:435px;\"><div class=\"cpp codecolorer\">...<br \/>\n<span class=\"me1\">std<\/span><span class=\"sy4\">::<\/span><span class=\"me2\">mutex<\/span> mut<span class=\"sy4\">;<\/span><br \/>\n<span class=\"kw4\">void<\/span> sharedFunc<span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span> <span class=\"br0\">&#123;<\/span><br \/>\n...<br \/>\n<span class=\"co1\">\/\/ ten kod mo\u017ce wykona\u0107 ka\u017cdy w\u0105tek wsp\u00f3\u0142bie\u017cnie<\/span><br \/>\nmut.<span class=\"me1\">lock<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy4\">;<\/span><br \/>\n<span class=\"co1\">\/\/ sekcja krytyczna<\/span><br \/>\nmut.<span class=\"me1\">unlock<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#125;<\/span><br \/>\n...<\/div><\/div>\n\n\n\n<p>Znasz ju\u017c i rozumiesz u\u017cycie powy\u017cszego kodu, prawda? Je\u015bli nie, odsy\u0142am do <a href=\"https:\/\/www.kompikownia.pl\/index.php\/2019\/02\/05\/aplikacje-wielowatkowe-w-jezyku-c\/\">poprzedniego artyku\u0142u m\u00f3wi\u0105cego o wsp\u00f3\u0142bie\u017cno\u015bci w C++.&nbsp;<\/a> Pomy\u015blmy, jakie problemy mog\u0105 tu wyst\u0105pi\u0107?<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Zakleszczenie &#8211; co to jest?<\/h3>\n\n\n\n<p>Najpopularniejszym problemem wyst\u0119puj\u0105cym wtedy, gdy korzystamy z wielu w\u0105tk\u00f3w i blokad jest <strong>zakleszczenie.<\/strong> Na czym ono polega?<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"555\" height=\"385\" src=\"https:\/\/www.kompikownia.pl\/wp-content\/uploads\/2019\/02\/deadlock-1.png\" alt=\"\" class=\"wp-image-746\"\/><\/figure><\/div>\n\n\n\n<p>Sp\u00f3jrz na powy\u017cszy rysunek. Mamy w nim dwa zasoby. Uznajmy, \u017ce s\u0105 to nasze sekcje krytyczne, czyli muteksy. W\u0105tek pierwszy zaj\u0105\u0142 sobie zas\u00f3b pierwszy. W tej chwili nikt inny opr\u00f3cz niego nie mo\u017ce z tego zasobu korzysta\u0107. Jednak\u017ce, do kontynuowania pracy w\u0105tek pierwszy potrzebuje zasobu drugiego. Tego zasobu nie mo\u017ce otrzyma\u0107, gdy\u017c jest on przydzielony w\u0105tkowi drugiemu. Ten z kolei czeka na zas\u00f3b pierwszy zajmowany, jak wiemy, przez w\u0105tek pierwszy. Taka cykliczna zale\u017cno\u015b\u0107 nigdy si\u0119 nie sko\u0144czy. W przyk\u0142adach pojawiaj\u0105cych si\u0119 w dalszej cz\u0119\u015bci tego artyku\u0142u niejednokrotnie zetkniemy si\u0119 z problemem zakleszcze\u0144 i b\u0119dziemy pr\u00f3bowali go rozwi\u0105za\u0107 najmniej radykalnie. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Brak zwolnienia muteksa &#8211; zakleszczenie<\/h3>\n\n\n\n<p>O czym mo\u017cemy zapomnie\u0107 pisz\u0105c zaawansowan\u0105 aplikacj\u0119 wielow\u0105tkow\u0105? Oczywi\u015bcie, o zwolnieniu muteksa, czyli u\u017cyciu metody unlock(). Co si\u0119 stanie w takim wypadku? Program przestanie dzia\u0142a\u0107. Ka\u017cdy nast\u0119pny w\u0105tek kt\u00f3ry dotrze do blokady nie b\u0119dzie m\u00f3g\u0142 wej\u015b\u0107 do sekcji krytycznej mimo i\u017c ta jest pusta. Stanie si\u0119 tak dlatego, gdy\u017c system operacyjny nie zostanie poinformowany o zwolnieniu sekcji krytycznej przez pierwszy w\u0105tek. Ba! Mo\u017cemy doprowadzi\u0107 do sytuacji, \u017ce w\u0105tek b\u0119dzie czeka\u0142 sam na siebie! Sp\u00f3jrz np.: na taki kod:<\/p>\n\n\n\n<div class=\"cppCompiler\">\n<div class=\"cppPoweredBy\">\nPowered By <a target=\"_blank\" href=\"https:\/\/coliru.stacked-crooked.com\/\" rel=\"noopener noreferrer\">Coliru Online Compiler<\/a> and <a target=\"_blank\" href=\"https:\/\/ace.c9.io\/\" rel=\"noopener noreferrer\">Ace editor<\/a>\n<\/div>\n<div class=\"cppEditorParent\">\n<pre class=\"cppEditor\" id=\"editor0\" style=\"width: 500px\">#include &lt;iostream&gt;\n#include &lt;thread&gt;\n#include &lt;mutex&gt;\n\nstd::mutex mut;\nvoid sharedFunc() {\n     for(int i = 0;i&lt;10;i++) {\n          mut.lock();\n          std::cout&lt;&lt;\"Obieg nr: \"&lt;&lt;i&lt;&lt;std::endl;\n     }\n}\nint main() {\n     std::thread thr(sharedFunc);\n     thr.join();\n     return 0;\n}\n<\/pre>\n\n<\/div>\n<pre id=\"editor0Result\"><\/pre>\n<\/div>\n\n\n\n<p>Uruchom powy\u017cszy program, klikaj\u0105c przycisk kompiluj. Nie przejmuj si\u0119 tym, \u017ce wynik nie pojawia si\u0119 od razu tylko po prostu poczekaj. Ujrzysz pewnie co\u015b w stylu: &#8222;execution expired&#8221;. Program nie zdo\u0142a\u0142 si\u0119 wykona\u0107 w przeznaczonym dla niego limicie czasu. Dlaczego? Przeanalizujmy jego dzia\u0142anie. W\u0105tek zaczyna wykonywanie funkcji sharedFunc. Wchodzi w p\u0119tl\u0119 for. Nast\u0119pnym krokiem jest wej\u015bcie do sekcji krytycznej i zablokowanie muteksa mut. Odwo\u0142uj\u0105c si\u0119 do rysunku przedstawionego wcze\u015bniej, mo\u017cemy powiedzie\u0107, \u017ce system operacyjny zarezerwowa\u0142 dla w\u0105tku zas\u00f3b mut. W\u0105tek ko\u0144czy pierwszy obieg p\u0119tli. Zaczynamy kolejny obieg. System operacyjny ju\u017c przydzieli\u0142 w\u0105tkowi jedyn\u0105 dost\u0119pn\u0105 sztuk\u0119 zasobu mut. Nie zosta\u0142 on zwr\u00f3cony do systemu, gdy\u017c nie wywo\u0142ali\u015bmy metody unlock. Teraz ten bezczelny w\u0105tek \u017c\u0105da jeszcze jednej sztuki zasobu! System operacyjny nie mo\u017ce jej w tej chwili przydzieli\u0107, wi\u0119c w\u0105tek musi sobie poczeka\u0107. I b\u0119dzie czeka\u0142 tak w niesko\u0144czono\u015b\u0107, gdy\u017c oczekuje tak naprawd\u0119 sam na siebie! A konkretnie: na zas\u00f3b, kt\u00f3ry sobie przydzieli\u0142 i o kt\u00f3rym zapomnia\u0142 \u017ce go ma. Doprowadzili\u015bmy do <strong>zakleszczenia.<\/strong><\/p>\n\n\n\n<p>Taki program mo\u017cna zamkn\u0105\u0107 jedynie brutalnymi metodami, takimi jak mened\u017cer zada\u0144 w Windowsie czy polecenie kill w Linuksie. Mniej wprawny u\u017cytkownik naszej aplikacji m\u00f3g\u0142by si\u0119 mocno wkurzy\u0107, a tego przecie\u017c nie chcemy, prawda?<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"wyjimutx\">Wyj\u0105tki i muteksy w C++ &#8211; wybuchowa mieszanka<\/h3>\n\n\n\n<p>Z pewno\u015bci\u0105&nbsp;s\u0142ysza\u0142e\u015b o wyj\u0105tkach. S\u0105 one bardzo fajn\u0105 metod\u0105 obs\u0142ugi b\u0142\u0119d\u00f3w w j\u0119zyku C++. Problem powstaje, gdy sytuacja wyj\u0105tkowa wyst\u0119puje w sekcji krytycznej. W momencie wyst\u0105pienia wyj\u0105tku wykonanie funkcji jest przerywane. Zwolni\u0107 muteks musimy zatem nie tylko na ko\u0144cu sekcji krytycznej, ale tak\u017ce w momencie obs\u0142ugi wyj\u0105tku. Je\u015bli tego nie zrobimy, doprowadzimy do zakleszczenia. Sp\u00f3jrz na poni\u017cszy przyk\u0142ad: <\/p>\n\n\n\n<div class=\"cppCompiler\">\n<div class=\"cppPoweredBy\">\nPowered By <a target=\"_blank\" href=\"https:\/\/coliru.stacked-crooked.com\/\" rel=\"noopener noreferrer\">Coliru Online Compiler<\/a> and <a target=\"_blank\" href=\"https:\/\/ace.c9.io\/\" rel=\"noopener noreferrer\">Ace editor<\/a>\n<\/div>\n<div class=\"cppEditorParent\">\n<pre class=\"cppEditor\" id=\"editor5\" style=\"width: 500px\">#include &lt;iostream&gt;\n#include &lt;thread&gt;\n#include &lt;mutex&gt;\n#include &lt;chrono&gt;\nusing std::cout;\nusing std::endl;\nstd::mutex mut;\nvoid exceptionMutexTest(int id) {\n    std::this_thread::sleep_for(std::chrono::milliseconds(400));\n    try {\n        mut.lock();\n        cout&lt;&lt;\"Wykonuj\u0119 sekcj\u0119 krytyczn\u0105 dla id: \"&lt;&lt;id&lt;&lt;endl;\n        std::this_thread::sleep_for(std::chrono::milliseconds(300));\n        if(id==1) throw \"Wyst\u0105pi\u0142 bardzo powa\u017cny problem\";\n        mut.unlock();\n    }\n    catch(const char* p) {\n        cout&lt;&lt;\"Wyst\u0105pi\u0142 wyj\u0105tek dla w\u0105tku: \"&lt;&lt;id&lt;&lt;\" o tre\u015bci: \"&lt;&lt;p&lt;&lt;endl;\n    }\n}\nint main()\n{\n    std::thread thr1(exceptionMutexTest,0);\n    std::this_thread::sleep_for(std::chrono::milliseconds(100));\n    std::thread thr2(exceptionMutexTest,1);\n    std::this_thread::sleep_for(std::chrono::milliseconds(100));\n    std::thread thr3(exceptionMutexTest,2);\n    thr1.join();\n    thr2.join();\n    thr3.join();\n    return 0;\n}\n<\/pre>\n\n<\/div>\n<pre class=\"EditorResult\" id=\"editor5Result\"><\/pre>\n<\/div>\n\n\n\n<p>Dostajemy po r\u0119kach znanym ju\u017c b\u0142\u0119dem naszego \u015brodowiska wykonawczego: &#8222;execution expired&#8221;. Gdy wyj\u0105tki nie wyst\u0119puj\u0105, wszystko dzia\u0142a prawid\u0142owo. W\u0105tek o ID=0 wykonuje swoj\u0105 prac\u0119 do ko\u0144ca. K\u0142opoty zaczynaj\u0105 si\u0119, gdy swoje zadania zaczyna realizowa\u0107 kolejny w\u0105tek. Powoduje on wyj\u0105tek. Programista w obs\u0142udze wyj\u0105tku zapomnia\u0142 zwolni\u0107 blokad\u0119 muteksa. Skutki tak lekkomy\u015blnego post\u0119powania wida\u0107 gdy do swojej pracy zabiera si\u0119 kolejny w\u0105tek. Nie mo\u017ce on nawet zacz\u0105\u0107 swojej pracy, poniewa\u017c poprzedni nie zwolni\u0142 blokady. B\u0119dziemy w stanie zakleszczenia i nic na to, jako u\u017cytkownicy programu nie poradzimy. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">lock_guard &#8211; prosta metoda dla zapominalskich w C++<\/h2>\n\n\n\n<p>Jak w prosty spos\u00f3b mo\u017cemy rozwi\u0105za\u0107 wszystkie powy\u017csze dolegliwo\u015bci? Programista obci\u0105\u017cony wieloma zadaniami mo\u017ce by\u0107 zapominalski i nie nale\u017cy dziwi\u0107 si\u0119, \u017ce czasami zapomni po sobie posprz\u0105ta\u0107. Tw\u00f3rcy j\u0119zyka C++ to wiedz\u0105 i dlatego wprowadzaj\u0105 coraz wi\u0119cej klas zgodnych ze wzorcem projektowym RAII (powiemy o nim wi\u0119cej wkr\u00f3tce) <\/p>\n\n\n\n<p>Jedn\u0105 z takich klas jest<a href=\"https:\/\/en.cppreference.com\/w\/cpp\/thread\/lock_guard\"> <\/a><strong><a href=\"https:\/\/en.cppreference.com\/w\/cpp\/thread\/lock_guard\">lock_guard.<\/a><\/strong> Co ona nam daje? Ot\u00f3\u017c, pilnuje aby muteks w odpowiednim momencie zosta\u0142 zwolniony. Takie zwolnienie odbywa si\u0119 wtedy, kiedy ko\u0144czy si\u0119 wykonanie danego bloku kodu. Spr\u00f3bujmy zastosowa\u0107 ten przyk\u0142ad w praktyce:<\/p>\n\n\n\n<div class=\"cppCompiler\">\n<div class=\"cppPoweredBy\">\nPowered By <a target=\"_blank\" href=\"https:\/\/coliru.stacked-crooked.com\/\" rel=\"noopener noreferrer\">Coliru Online Compiler<\/a> and <a target=\"_blank\" href=\"https:\/\/ace.c9.io\/\" rel=\"noopener noreferrer\">Ace editor<\/a>\n<\/div>\n<div class=\"cppEditorParent\">\n<pre class=\"cppEditor\" id=\"editor1\" style=\"width: 500px\">#include &lt;iostream&gt;\n#include &lt;thread&gt;\n#include &lt;mutex&gt;\n\nstd::mutex mut;\nvoid sharedFunc() {\n     for(int i = 0;i&lt;10;i++) {\n          std::lock_guard&lt;std::mutex&gt; lock(mut);\n          std::cout&lt;&lt;\"Obieg nr: \"&lt;&lt;i&lt;&lt;std::endl;\n     }\n}\nint main() {\n     std::thread thr(sharedFunc);\n     thr.join();\n     return 0;\n}\n<\/pre>\n\n<\/div>\n<pre class=\"EditorResult\" id=\"editor1Result\"><\/pre>\n<\/div>\n\n\n\n<p>Zmiana nast\u0105pi\u0142a w linijce 8. U\u017cyli\u015bmy nowego, wielokrotnie kilka razy wspominanego w tym rozdziale wspomagacza lock_guard. Uruchom powy\u017cszy program. Jak widzisz, wykonuje si\u0119 teraz bez problem\u00f3w. S\u0142ynny lock_guard pilnuje aby muteks zosta\u0142 zwolniony za ka\u017cdym razem, kiedy obieg p\u0119tli si\u0119&nbsp;ko\u0144czy.<\/p>\n\n\n\n<p>Co najwa\u017cniejsze, lock_guard zwalnia muteks tak\u017ce wtedy, gdy wyst\u0105pi wyj\u0105tek \ud83d\ude42 Przer\u00f3b program z rozdzia\u0142u <a href=\"#wyjimutx\">Wyj\u0105tki i muteksy w C++ \u2013 wybuchowa mieszanka&#8221;<\/a> tak, aby korzysta\u0142 z lock_guard i sam przekonaj si\u0119, \u017ce to rzeczywi\u015bcie dzia\u0142a. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Lock_guard niezast\u0105pionym narz\u0119dziem obs\u0142ugi muteks\u00f3w w C++?<\/h3>\n\n\n\n<p>Je\u015bli pobawisz si\u0119 chwil\u0119 lock_guardem, zauwa\u017cysz, \u017ce brakuje mu kilku z poznanych do tej pory mo\u017cliwo\u015bci muteks\u00f3w. Owszem, lock_guard pilnuje, aby blokada na muteksie zosta\u0142a zar\u00f3wno za\u0142o\u017cona jak i zdj\u0119ta automatycznie. Ale zauwa\u017c, \u017ce tracimy tym samym mo\u017cliwo\u015b\u0107 r\u0119cznego zarz\u0105dzania stanem muteksa. U\u017cywaj\u0105c lock_guard, nie mo\u017cemy jawnie wywo\u0142a\u0107 metody unlock. M\u00f3wi o tym dokumentacja C++, kt\u00f3rej fragment przytaczam poni\u017cej. <\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"854\" height=\"283\" src=\"https:\/\/www.kompikownia.pl\/wp-content\/uploads\/2019\/02\/Screenshot_20190209_202345.png\" alt=\"\" class=\"wp-image-719\"\/><figcaption>\u0179r\u00f3d\u0142o: <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/thread\/lock_guard\">cppreference.com<\/a><\/figcaption><\/figure>\n\n\n\n<p>Lock_guard nie zawiera \u017cadnych metod. Posiada jedynie konstruktor i destruktor.<\/p>\n\n\n\n<p>Zastan\u00f3wmy si\u0119, czy w og\u00f3le potrzebujemy mo\u017cliwo\u015bci r\u0119cznego blokowania i odblokowywania muteks\u00f3w? Teoretycznie Lock_guard w 99% mo\u017ce nas zabezpieczy\u0107&nbsp;przed jakimikolwiek sytuacjom hazardowym\/niebezpiecznym. (O tym 1%, kiedy standardowy lock_guard nie oka\u017ce si\u0119 skuteczny powiem w dalszej cz\u0119\u015bci artyku\u0142u). Ale jego u\u017cycie zabija jednocze\u015bnie optymalizacj\u0119. A tym samym mo\u017cemy utraci\u0107 jedn\u0105 z najwa\u017cniejszych zalet w\u0105tk\u00f3w &#8211; mo\u017cliwo\u015b\u0107 wsp\u00f3\u0142bie\u017cnego wykonania. Dlaczego? Przeanalizuj poni\u017cszy listing.<\/p>\n\n\n<div class=\"codecolorer-container cpp default\" style=\"overflow:auto;white-space:nowrap;width:435px;\"><div class=\"cpp codecolorer\">std<span class=\"sy4\">::<\/span><span class=\"me2\">mutex<\/span> mut<span class=\"sy4\">;<\/span><br \/>\n<span class=\"kw4\">void<\/span> func<span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span> <span class=\"br0\">&#123;<\/span><br \/>\n<span class=\"br0\">&#91;<\/span><span class=\"nu0\">1<\/span><span class=\"br0\">&#93;<\/span>... <span class=\"co1\">\/\/ kod, kt\u00f3ry mo\u017ce si\u0119 wykonywa\u0107 r\u00f3wnocze\u015bnie w wielu w\u0105tkach bez blokad<\/span><br \/>\n<span class=\"br0\">&#91;<\/span><span class=\"nu0\">2<\/span><span class=\"br0\">&#93;<\/span> std<span class=\"sy4\">::<\/span><span class=\"me2\">lock_guard<\/span><span class=\"sy1\">&lt;<\/span>std<span class=\"sy4\">::<\/span><span class=\"me2\">mutex<\/span><span class=\"sy1\">&gt;<\/span> lock<span class=\"br0\">&#40;<\/span>mut<span class=\"br0\">&#41;<\/span><span class=\"sy4\">;<\/span><br \/>\n<span class=\"br0\">&#91;<\/span><span class=\"nu0\">3<\/span><span class=\"br0\">&#93;<\/span>... <span class=\"co1\">\/\/ sekcja krytyczna, kod kt\u00f3ry mo\u017ce wykonywa\u0107 tylko jeden w\u0105tek na raz<\/span><br \/>\n<span class=\"kw3\">cout<\/span><span class=\"sy1\">&lt;&lt;<\/span><span class=\"st0\">&quot;Wynik operacji to: &quot;<\/span><span class=\"sy1\">&lt;&lt;<\/span>...<span class=\"sy1\">&lt;&lt;<\/span>endl<span class=\"sy4\">;<\/span><br \/>\n<span class=\"co1\">\/\/ w tym momencie nasza sekcja krytyczna si\u0119 ko\u0144czy, ale blokada nie zostaje zdj\u0119ta <\/span><br \/>\n<span class=\"br0\">&#91;<\/span><span class=\"nu0\">4<\/span><span class=\"br0\">&#93;<\/span>... <span class=\"co1\">\/\/ kod, kt\u00f3ry mo\u017ce wykonywa\u0107 si\u0119 r\u00f3wnocze\u015bnie w wielu watkach bez blokad<\/span><br \/>\n<span class=\"br0\">&#125;<\/span><\/div><\/div>\n\n\n\n<p>Aby \u0142atwiej by\u0142o om\u00f3wi\u0107 powy\u017cszy przyk\u0142ad, podzieli\u0142em go na sekcj\u0119, [1], [2], [3] i [4]. <\/p>\n\n\n\n<p>Funkcja func si\u0119 uruchamia. Za\u0142\u00f3\u017cmy, \u017ce jednocze\u015bnie wykonuje j\u0105 kilka w\u0105tk\u00f3w. Cz\u0119\u015b\u0107 oznaczon\u0105 jako [1] wszystkie w\u0105tki mog\u0105 wykona\u0107 wsp\u00f3\u0142bie\u017cnie. \u017baden muteks czy inna blokada nie uniemo\u017cliwia takiej czynno\u015bci. Dopiero gdy kt\u00f3ry\u015b w\u0105tek dotrze do [2] czyli utworzenia obiektu lock, blokada jest zak\u0142adana. Dalsz\u0105 podr\u00f3\u017c odb\u0119dzie tylko jeden w\u0105tek a pozosta\u0142e s\u0105 wstrzymywane w tym punkcie. Sekcja krytyczna obejmuje cz\u0119\u015b\u0107 [3]. U\u017cywaj\u0105c lock_guard nie mamy mo\u017cliwo\u015bci poinformowania systemu, \u017ce w momencie dotarcia do ko\u0144ca cz\u0119\u015bci 3 ko\u0144czy si\u0119 jednocze\u015bnie sekcja krytyczna. Mimo, \u017ce kod cz\u0119\u015bci [4] w \u017caden spos\u00f3b nie koliduje z wykonaniem cz\u0119\u015bci w sekcji krytycznej, to pozosta\u0142e w\u0105tki s\u0105 w dalszym ci\u0105gu wstrzymywane. Dopiero, gdy w\u0105tek aktualnie znajduj\u0105cy si\u0119 w sekcji krytycznej dotrze do ko\u0144ca funkcji, muteks zostanie odblokowany i nast\u0119pny w\u0105tek zacznie wykonywa\u0107 swoj\u0105 prac\u0119.<\/p>\n\n\n\n<p>Ach, tracimy tyle pr\u0119dko\u015bci, marnujemy tyle mocy procesora. Dodatkowo, \u0142amiemy naczeln\u0105 zasad\u0119 m\u00f3wi\u0105c\u0105 o tym \u017ce sekcja krytyczna powinna by\u0107 tak ma\u0142a jak to tylko mo\u017cliwe. Czy naprawd\u0119 musimy wraca\u0107 do zwyk\u0142ych muteks\u00f3w? Mamy co prawda nad nimi pe\u0142n\u0105 kontrol\u0119, ale s\u0105 uci\u0105\u017cliwe w u\u017cyciu i b\u0142\u0119dogenne. Czy rzeczywi\u015bcie chc\u0105c uzyska\u0107 najwy\u017csz\u0105 wydajno\u015b\u0107 nie mo\u017cemy korzysta\u0107 z udogodnie\u0144 w postaci automatycznego odblokowywania muteksu dostarczanego przez lock_guard? Ot\u00f3\u017c, niekoniecznie. Istnieje klasa kt\u00f3ra posiada zalety lock_guard, a jednocze\u015bnie pozwala na r\u0119czne zarz\u0105dzanie stanem muteksa. Poznaj: <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/thread\/unique_lock\">unique_lock<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">unique_lock &#8211; i synchronizacja w\u0105tk\u00f3w staje si\u0119 prostsza<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Jak u\u017cy\u0107 unique_lock w synchronizacji w\u0105tk\u00f3w C++?<\/h3>\n\n\n\n<p>Jak u\u017cy\u0107 unique_lock? Zobaczysz to, jak przeanalizujemy poni\u017cszy kod:<\/p>\n\n\n\n<div class=\"cppCompiler\">\n<div class=\"cppPoweredBy\">\nPowered By <a target=\"_blank\" href=\"https:\/\/coliru.stacked-crooked.com\/\" rel=\"noopener noreferrer\">Coliru Online Compiler<\/a> and <a target=\"_blank\" href=\"https:\/\/ace.c9.io\/\" rel=\"noopener noreferrer\">Ace editor<\/a>\n<\/div>\n<div class=\"cppEditorParent\">\n<pre class=\"cppEditor\" id=\"editor2\" style=\"width: 500px\">#include &lt;iostream&gt;\n#include &lt;thread&gt;\n#include &lt;mutex&gt;\n#include &lt;chrono&gt;\n#include &lt;vector&gt;\n#include &lt;algorithm&gt;\nusing std::cout;\nusing std::endl;\nconst int THREAD_COUNT = 3;\nstd::mutex mut;\nvoid sharedFunc(int nr) {\n    std::this_thread::sleep_for(std::chrono::milliseconds(500)); \/\/ symulacja skomplikowanych operacji\n    cout&lt;&lt;\"Watek: \"&lt;&lt;nr&lt;&lt;\" sekcja 1 uko\u0144czona\"&lt;&lt;endl;\n    std::unique_lock&lt;std::mutex&gt; lock(mut);\n    std::this_thread::sleep_for(std::chrono::milliseconds(700)); \/\/ symulacja skomplikowanych operacji\n    cout&lt;&lt;\"W\u0105tek: \"&lt;&lt;nr&lt;&lt;\" sekcja 2 uko\u0144czona\"&lt;&lt;endl;\n    lock.unlock();\n    std::this_thread::sleep_for(std::chrono::milliseconds(500)); \/\/ symulacja skomplikowanych operacji\n    cout&lt;&lt;\"W\u0105tek: \"&lt;&lt;nr&lt;&lt;\" sekcja 3 uko\u0144czona\"&lt;&lt;endl;\n}\n\nint main() {\n    auto start = std::chrono::system_clock::now();\n    std::vector&lt;std::thread&gt; threads;\n    for(int i = 0;i&lt;THREAD_COUNT;i++) {\n        threads.push_back(std::thread(sharedFunc,i));\n    }\n    for_each(threads.begin(),threads.end(),[](std::thread&amp; v)-&gt;void{v.join();});\n    auto end = std::chrono::system_clock::now();\n    cout&lt;&lt;\"Program wykonywa\u0142 si\u0119 :\"&lt;&lt;std::chrono::duration_cast&lt;std::chrono::milliseconds&gt;(end-start).count()&lt;&lt;\" milisekund\"&lt;&lt;endl;\n    return 0;\n}\n<\/pre>\n\n<\/div>\n<pre class=\"EditorResult\" id=\"editor2Result\"><\/pre>\n<\/div>\n\n\n\n<p>Uruchom powy\u017cszy program. Algorytm jest taki sam jak w poprzednim rozdziale, w kt\u00f3rym omawiali\u015bmy jedn\u0105 z podstawowych wad lock_guard. Jedyna r\u00f3\u017cnica jest taka, \u017ce zast\u0105pili\u015bmy lock_guard unique_lockiem. Sp\u00f3jrz na linijk\u0119 14. W\u0142a\u015bnie w tamtym miejscu tworzymy obiekt unique_lock. Tutaj tak\u017ce jest zak\u0142adana jest blokada. W linijce 17 zdejmujemy blokad\u0119 r\u0119cznie. Dzi\u0119ki temu gdy w\u0105tek A opu\u015bci sekcj\u0119 krytyczn\u0105, oczekuj\u0105cy w\u0105tek B mo\u017ce do niej natychmiastowo wej\u015b\u0107, mimo i\u017c w\u0105tek A nie doszed\u0142 jeszcze do ko\u0144ca funkcji sharedFunc. Zauwa\u017cysz to tak\u017ce po wynikach dzia\u0142ania programu, je\u015bli go uruchomisz.<\/p>\n\n\n\n<p>A co je\u015bli usuniemy linijk\u0119 17? Nic si\u0119 nie stanie. Wtedy unique_lock zachowa si\u0119 dok\u0142adnie w taki sam spos\u00f3b, w jaki zachowa\u0142by si\u0119 lock_guard &#8211; zdejmie blokad\u0119&nbsp;muteksa w momencie kiedy w\u0105tek dojdzie do ko\u0144ca funkcji. Spr\u00f3buj i przekonaj si\u0119 sam! <\/p>\n\n\n\n<p>Sp\u00f3jrz na r\u00f3\u017cnic\u0119 czas\u00f3w. Gdy zastosowali\u015bmy optymalizacj\u0119 z u\u017cyciem unique_lock, program wykonywa\u0142 si\u0119 ok. 3100 milisekund. Gdyby\u015bmy u\u017cyli lock_guard, program wykona si\u0119 w czasie ok. 4100 milisekund. R\u00f3\u017cnica b\u0119dzie ros\u0142a wraz ze wzrostem liczby w\u0105tk\u00f3w.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Kiedy <strong>musimy<\/strong> u\u017cy\u0107 unique_lock? <\/h3>\n\n\n\n<p>Istnieje kilka przypadk\u00f3w, kiedy najprostszym mo\u017cliwym wspomagaczem, kt\u00f3rego mo\u017cemy u\u017cy\u0107, jest unique_lock. Wyobra\u017a sobie nast\u0119puj\u0105c\u0105 sytuacj\u0119.<\/p>\n\n\n\n<p>Tworzymy aplikacj\u0119 dla banku. Mamy miliony klient\u00f3w. Si\u0142\u0105 rzeczy musimy wykorzystywa\u0107 ca\u0142\u0105 moc dost\u0119pnych maszyn. Musimy wykona\u0107 przelew z jednego konta na drugie. Pieni\u0105dze s\u0105 rzecz\u0105 wra\u017cliw\u0105 i nie chcemy, aby pojawi\u0142y si\u0119 jakiekolwiek problemy typu: zdublowane pieni\u0105dze na jednym z kont itd. Musimy zabezpieczy\u0107 tak\u0105 operacj\u0119 za pomoc\u0105 blokad. Jak by\u015bmy to zrobili u\u017cywaj\u0105c lock_guard? <\/p>\n\n\n\n<div class=\"cppCompiler\">\n<div class=\"cppPoweredBy\">\nPowered By <a target=\"_blank\" href=\"https:\/\/coliru.stacked-crooked.com\/\" rel=\"noopener noreferrer\">Coliru Online Compiler<\/a> and <a target=\"_blank\" href=\"https:\/\/ace.c9.io\/\" rel=\"noopener noreferrer\">Ace editor<\/a>\n<\/div>\n<div class=\"cppEditorParent\">\n<pre class=\"cppEditor\" id=\"editor3\" style=\"width: 500px\">#include &lt;iostream&gt;\n#include &lt;thread&gt;\n#include &lt;mutex&gt;\n#include &lt;chrono&gt;\nusing std::cout;\nusing std::endl;\nclass BankAccount {\n    public:\n    BankAccount(int __balance) : balance(__balance) {}\n    void transferFrom(BankAccount&amp; from, int value) {\n        \/\/ musimy zablokowa\u0107 obydwa muteksy, gdy\u017c ka\u017cdy transfer bankAccount uruchomiony jest w oddzielnym w\u0105tku\n        std::lock_guard&lt;std::mutex&gt; lock1(from.mut);\n        std::this_thread::sleep_for(std::chrono::milliseconds(300)); \/\/ za\u0142\u00f3\u017cmy, \u017ce co\u015b w tej chwili jest wykonywane\n        std::lock_guard&lt;std::mutex&gt; lock2(mut);\n        from.balance -= value;\n        balance += value;\n    }\n    int getBalance() {return balance;}\nprivate:\n    int balance;\n    std::mutex mut;\n    \/\/ inne rzeczy\n};\n\nint main()\n{\n    BankAccount b1(400);\n    cout&lt;&lt;\"Konto pierwsze ma : \"&lt;&lt;b1.getBalance()&lt;&lt;\" PLN \u015brodk\u00f3w\"&lt;&lt;endl;\n    BankAccount b2(500);\n    cout&lt;&lt;\"Konto drugie ma : \"&lt;&lt;b2.getBalance()&lt;&lt;\" PLN \u015brodk\u00f3w\"&lt;&lt;endl;\n    std::thread thr1(&amp;BankAccount::transferFrom,std::ref(b1),std::ref(b2),100);\n    std::thread thr2(&amp;BankAccount::transferFrom,std::ref(b2),std::ref(b1),200);\n    thr1.join();\n    thr2.join();\n    cout&lt;&lt;endl&lt;&lt;\"Po operacjach\"&lt;&lt;endl;\n    cout&lt;&lt;\"Konto pierwsze ma : \"&lt;&lt;b1.getBalance()&lt;&lt;\" PLN \u015brodk\u00f3w\"&lt;&lt;endl;    \n    cout&lt;&lt;\"Konto drugie ma : \"&lt;&lt;b2.getBalance()&lt;&lt;\" PLN \u015brodk\u00f3w\"&lt;&lt;endl;\n    return 0;\n}\n<\/pre>\n\n<\/div>\n<pre class=\"EditorResult\" id=\"editor3Result\"><\/pre>\n<\/div>\n\n\n\n<p>Zarz\u0105dzaniem kontami zajmuje si\u0119 klasa BankAccount. Metoda transferFrom pozwala na bezkonfliktowe przeniesienie pieni\u0119dzy z innego konta na aktualne. Z ka\u017cdym kontem powi\u0105zany jest muteks, kt\u00f3ry w momencie operowania na danym koncie powinien by\u0107 zablokowany Transfer pieni\u0119dzy z jednego konta na drugie modyfikuje bilans na obydwu kontach. W zwi\u0105zku z tym musimy zablokowa\u0107 obydwa muteksy. Linijka 13, w kt\u00f3rej znajduje si\u0119 sleep_for, symuluje dodatkowe operacje zwi\u0105zane z zapewnieniem wy\u0142\u0105cznego dost\u0119pu do danego konta. <\/p>\n\n\n\n<p>Uruchom powy\u017cszy program. Co si\u0119 dzieje? Dostajemy komunikat: execution expired. Oznacza to, \u017ce w kt\u00f3rym\u015b miejscu dosz\u0142o do zakleszczenia. Co si\u0119 mog\u0142o sta\u0107? <\/p>\n\n\n\n<p>Sp\u00f3jrz na linijki 12, 13 i 14. Pierwszy w\u0105tek zak\u0142ada blokad\u0119 na konto drugie oraz wykonuje swoje zwi\u0105zane z tym operacje. Nast\u0119pnie blokada zak\u0142adana jest na konto pierwsze. Drugi w\u0105tek wykonuje takie same czynno\u015bci ,tylko w odwrotnej kolejno\u015bci. Dochodzi do zakleszczenia. Pierwszy w\u0105tek ju\u017c zablokowa\u0142 obydwa muteksy w momencie, kiedy drugi w\u0105tek pr\u00f3buje uzyska\u0107 wy\u0142\u0105czno\u015b\u0107. Tym samym obydwa w\u0105tki czekaj\u0105 na siebie nawzajem i raczej nigdy si\u0119 nie doczekaj\u0105 \ud83d\ude41 Do kosza z tym rozwi\u0105zaniem. (Na samym ko\u0144cu tego artyku\u0142u poka\u017c\u0119, jak mo\u017cna u\u017cy\u0107 lock_guard, aby powy\u017cszy program dzia\u0142a\u0142)<\/p>\n\n\n\n<p>Jak rozwi\u0105za\u0107 ten problem? Musimy u\u017cy\u0107 unique_lock oraz jednej z trzech specjalistycznych strategii blokuj\u0105cych: <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/thread\/lock_tag\">defer_lock<\/a>. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">unique_lock i defer_lock &#8211; rozwi\u0105zujemy problem zakleszczenia<\/h3>\n\n\n\n<div class=\"cppCompiler\">\n<div class=\"cppPoweredBy\">\nPowered By <a target=\"_blank\" href=\"https:\/\/coliru.stacked-crooked.com\/\" rel=\"noopener noreferrer\">Coliru Online Compiler<\/a> and <a target=\"_blank\" href=\"https:\/\/ace.c9.io\/\" rel=\"noopener noreferrer\">Ace editor<\/a>\n<\/div>\n<div class=\"cppEditorParent\">\n<pre class=\"cppEditor\" id=\"editor4\" style=\"width: 500px\">#include &lt;iostream&gt;\n#include &lt;thread&gt;\n#include &lt;mutex&gt;\n#include &lt;chrono&gt;\nusing std::cout;\nusing std::endl;\nclass BankAccount {\n    public:\n    BankAccount(int __balance) : balance(__balance) {}\n    void transferFrom(BankAccount&amp; from, int value) {\n        \/\/ musimy zablokowa\u0107 obydwa muteksy, gdy\u017c ka\u017cdy transfer bankAccount uruchomiony jest w oddzielnym w\u0105tku\n        std::unique_lock&lt;std::mutex&gt; lock1(from.mut,std::defer_lock);\n        std::this_thread::sleep_for(std::chrono::milliseconds(300)); \/\/ za\u0142\u00f3\u017cmy, \u017ce co\u015b w tej chwili jest wykonywane\n        std::unique_lock&lt;std::mutex&gt; lock2(mut,std::defer_lock);\n        std::lock(lock1,lock2);\n        from.balance -= value;\n        balance += value;\n    }\n    int getBalance() {return balance;}\n    private:\n    int balance;\n    std::mutex mut;\n    \/\/ inne rzeczy\n};\n\nint main()\n{\n    BankAccount b1(400);\n    cout&lt;&lt;\"Konto pierwsze ma : \"&lt;&lt;b1.getBalance()&lt;&lt;\" PLN \u015brodk\u00f3w\"&lt;&lt;endl;\n    BankAccount b2(500);\n    cout&lt;&lt;\"Konto drugie ma : \"&lt;&lt;b2.getBalance()&lt;&lt;\" PLN \u015brodk\u00f3w\"&lt;&lt;endl;\n    std::thread thr1(&amp;BankAccount::transferFrom,std::ref(b1),std::ref(b2),100);\n    std::thread thr2(&amp;BankAccount::transferFrom,std::ref(b2),std::ref(b1),200);\n    thr1.join();\n    thr2.join();\n    cout&lt;&lt;endl&lt;&lt;\"Po operacjach\"&lt;&lt;endl;\n    cout&lt;&lt;\"Konto pierwsze ma : \"&lt;&lt;b1.getBalance()&lt;&lt;\" PLN \u015brodk\u00f3w\"&lt;&lt;endl;    \n    cout&lt;&lt;\"Konto drugie ma : \"&lt;&lt;b2.getBalance()&lt;&lt;\" PLN \u015brodk\u00f3w\"&lt;&lt;endl;\n    return 0;\n}\n<\/pre>\n<\/div>\n<pre class=\"EditorResult\" id=\"editor4Result\"><\/pre>\n<\/div>\n\n\n\n<p>Co zmieni\u0142o si\u0119 w kodzie? W sumie niewiele. Linijka 12 i 14 si\u0119 zmieni\u0142a. Wcze\u015bniej u\u017cywali\u015bmy lock_guard, a teraz u\u017cywamy unique_lock. W przeciwie\u0144stwie do poprzednich przyk\u0142ad\u00f3w, podali\u015bmy konstruktorowi dodatkowy argument: std::defer_lock. Co to jest? Ot\u00f3\u017c, jest to jedna z trzech dost\u0119pnych strategii blokowania. std::defer_lock zapewnia, \u017ce blokada <strong>nie&nbsp;zostanie<\/strong> <strong>zalo\u017cona<\/strong> wraz z utworzeniem obiektu unique_lock. Nie tracimy jednak\u017ce mo\u017cliwo\u015bci zwolnienia blokady wraz z ko\u0144cem bloku kodu.<\/p>\n\n\n\n<p>Blokad\u0119 zak\u0142adamy r\u0119cznie w linijce 15. S\u0142u\u017cy do tego specjalna funkcja: <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/thread\/lock\">lock.<\/a> Czym r\u00f3\u017cni si\u0119 od r\u0119cznego blokowania muteksa za pomoc\u0105 wbudowanej w niego metody lock? Funkcja lock zapewnia, \u017ce wszystkie muteksy podane jako argumenty funkcji lock zostan\u0105 zablokowane, <strong>przy&nbsp;czym nigdy nie&nbsp;powstanie&nbsp;zakleszczenie.<\/strong><\/p>\n\n\n\n<p>Dzi\u0119ki temu nasz stworzony przed chwil\u0105 bank mo\u017ce prawid\u0142owo dzia\u0142a\u0107 \ud83d\ude42<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Strategie blokuj\u0105ce<\/h2>\n\n\n\n<p>Z poprzedniego rozdzia\u0142u dowiedzieli\u015bmy si\u0119, \u017ce mo\u017cemy modyfikowa\u0107 zachowanie niekt\u00f3rych klas blokuj\u0105cych, takich jak unique_lock. Mamy do wyboru nast\u0119puj\u0105ce rodzaje zachowa\u0144: <\/p>\n\n\n\n<ul><li>Zachowanie domy\u015b\u0142ne &#8211; blokada zak\u0142adana jest w chwili utworzenia obiektu i zdejmowana w chwili opuszczenia bloku kodu\/funkcji\/metody<\/li><li><a href=\"https:\/\/en.cppreference.com\/w\/cpp\/thread\/lock_tag\">std::defer_lock<\/a> &#8211; nie zak\u0142ada blokady przy tworzeniu obiektu <\/li><li><a href=\"https:\/\/en.cppreference.com\/w\/cpp\/thread\/lock_tag\">std::try_to_lock<\/a> &#8211; pr\u00f3buje za\u0142o\u017cy\u0107 blokad\u0119. Wynik mo\u017cemy sprawdzi\u0107 za pomoc\u0105 metody owns_lock. Zalet\u0105 zastosowania tej strategii jest to, \u017ce w\u0105tek mo\u017ce zaj\u0105\u0107 si\u0119 inn\u0105 prac\u0105 w chwili, gdy nie uda\u0142o mu si\u0119 dosta\u0107 do sekcji krytycznej. <\/li><li><a href=\"https:\/\/en.cppreference.com\/w\/cpp\/thread\/lock_tag\">std::adopt_lock<\/a> &#8211; przyjmuje, \u017ce blokada zosta\u0142a za\u0142o\u017cona przez aktualnie wykonywany w\u0105tek. Strategia ta jedynie przejmuje kontrol\u0119 nad muteksem dbaj\u0105c o to, aby zosta\u0142 w odpowiedniej chwili zwolniony. <\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">std::adopt_lock &#8211; przyk\u0142ad u\u017cycia<\/h3>\n\n\n\n<p>Obieca\u0142em, \u017ce poka\u017c\u0119 jak przerobi\u0107 przyk\u0142ad z bankiem tak, aby\u015bmy mogli u\u017cy\u0107 lock_guard. Ot\u00f3\u017c, podobnie jak mo\u017cemy zmieni\u0107 zachowanie unique_lock, tak samo mo\u017cemy wp\u0142yn\u0105\u0107 na dzia\u0142anie lock_guard. Domy\u015blne zachowanie, jak ju\u017c udowodnili\u015bmy, nie sprawdzi si\u0119. A co, je\u015bli u\u017cyliby\u015bmy strategii adopt_lock? Zobaczmy<\/p>\n\n\n\n<div class=\"cppCompiler\">\n<div class=\"cppPoweredBy\">\nPowered By <a target=\"_blank\" href=\"https:\/\/coliru.stacked-crooked.com\/\" rel=\"noopener noreferrer\">Coliru Online Compiler<\/a> and <a target=\"_blank\" href=\"https:\/\/ace.c9.io\/\" rel=\"noopener noreferrer\">Ace editor<\/a>\n<\/div>\n<div class=\"cppEditorParent\">\n<pre class=\"cppEditor\" id=\"editor6\" style=\"width: 500px\">#include &lt;iostream&gt;\n#include &lt;thread&gt;\n#include &lt;mutex&gt;\n#include &lt;chrono&gt;\nusing std::cout;\nusing std::endl;\nclass BankAccount {\n    public:\n    BankAccount(int __balance) : balance(__balance) {}\n    void transferFrom(BankAccount&amp; from, int value) {\n        \/\/ musimy zablokowa\u0107 obydwa muteksy, gdy\u017c ka\u017cdy transfer bankAccount uruchomiony jest w oddzielnym w\u0105tku\n        std::lock(from.mut,mut);\n        std::lock_guard&lt;std::mutex&gt; lock2(mut,std::adopt_lock);\n        std::this_thread::sleep_for(std::chrono::milliseconds(300)); \/\/ za\u0142\u00f3\u017cmy, \u017ce co\u015b w tej chwili jest wykonywane\n        std::lock_guard&lt;std::mutex&gt; lock1(from.mut,std::adopt_lock);\n        from.balance -= value;\n        balance += value;\n    }\n    int getBalance() {return balance;}\n    private:\n    int balance;\n    std::mutex mut;\n    \/\/ inne rzeczy\n};\n\nint main()\n{\n    BankAccount b1(400);\n    cout&lt;&lt;\"Konto pierwsze ma : \"&lt;&lt;b1.getBalance()&lt;&lt;\" PLN \u015brodk\u00f3w\"&lt;&lt;endl;\n    BankAccount b2(500);\n    cout&lt;&lt;\"Konto drugie ma : \"&lt;&lt;b2.getBalance()&lt;&lt;\" PLN \u015brodk\u00f3w\"&lt;&lt;endl;\n    std::thread thr1(&amp;BankAccount::transferFrom,std::ref(b1),std::ref(b2),100);\n    std::thread thr2(&amp;BankAccount::transferFrom,std::ref(b2),std::ref(b1),200);\n    thr1.join();\n    thr2.join();\n    cout&lt;&lt;endl&lt;&lt;\"Po operacjach\"&lt;&lt;endl;\n    cout&lt;&lt;\"Konto pierwsze ma : \"&lt;&lt;b1.getBalance()&lt;&lt;\" PLN \u015brodk\u00f3w\"&lt;&lt;endl;    \n    cout&lt;&lt;\"Konto drugie ma : \"&lt;&lt;b2.getBalance()&lt;&lt;\" PLN \u015brodk\u00f3w\"&lt;&lt;endl;\n    return 0;\n}\n<\/pre>\n<\/div>\n<pre class=\"EditorResult\" id=\"editor6Result\"><\/pre>\n<\/div>\n\n\n\n<p>Jak zachowuj\u0105 si\u0119 blokady w tym wypadku? U\u017cywamy funkcji lock, tym razem bezpo\u015brednio na muteksach (linijka 12). Funkcja ta zablokuje obydwa muteksy dla w\u0105tku tak, aby nie wyst\u0105pi\u0142o zakleszczenie. W kolejnej linijce mamy utworzenie obiektu klasy lock_guard. Tym razem jednak\u017ce stosujemy strategi\u0119 adopt_lock. Lock_guard nie pr\u00f3buje za\u0142o\u017cy\u0107&nbsp;blokady, gdy\u017c ta zosta\u0142a wcze\u015bniej za\u0142o\u017cona r\u0119cznie przez funkcj\u0119 lock. Wg obranej strategii, pr\u00f3buje natomiast &#8222;adoptowa\u0107&#8221; muteks, przej\u0105\u0107 nad nim kontrol\u0119. Blokada jest za\u0142o\u017cona. Lock_guard b\u0119dzie dba\u0142 tylko o to, aby zosta\u0142a zwolniona. Podobne zadanie pe\u0142ni linijka 15 w odniesieniu do drugiego muteksu.<br><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">std::try_lock &#8211; przyk\u0142ad u\u017cycia<\/h3>\n\n\n\n<div class=\"cppCompiler\">\n<div class=\"cppPoweredBy\">\nPowered By <a target=\"_blank\" href=\"https:\/\/coliru.stacked-crooked.com\/\" rel=\"noopener noreferrer\">Coliru Online Compiler<\/a> and <a target=\"_blank\" href=\"https:\/\/ace.c9.io\/\" rel=\"noopener noreferrer\">Ace editor<\/a>\n<\/div>\n<div class=\"cppEditorParent\">\n<pre class=\"cppEditor\" id=\"editor7\" style=\"width: 500px\">#include &lt;iostream&gt;\n#include &lt;thread&gt;\n#include &lt;mutex&gt;\n#include &lt;chrono&gt;\n#include &lt;vector&gt;\n#include &lt;algorithm&gt;\nconst int THREAD_COUNT = 3;\nstd::mutex mut;\nstd::mutex mut2;\nusing std::cout;\nusing std::endl;\nint doneWork = 0;\nint otherWork = 0;\nvoid exampleFunc(int i) {\n    int result = 0;\n    int actualThreadWork = 0;\n    while(result==0) {\n        std::unique_lock&lt;std::mutex&gt; lock(mut,std::try_to_lock);\n        if(lock.owns_lock()) {\n            cout&lt;&lt;\"W\u0105tek :\"&lt;&lt;i&lt;&lt;\" posiada wy\u0142\u0105czno\u015b\u0107. Wykonuj\u0119 akcje\"&lt;&lt;endl;\n            std::this_thread::sleep_for(std::chrono::milliseconds(400));\n            result++;\n            doneWork++;\n            otherWork += actualThreadWork;\n            cout&lt;&lt;\"Prac\u0119 wykona\u0142o: \"&lt;&lt;doneWork&lt;&lt;\" w\u0105tk\u00f3w\"&lt;&lt;endl;\n            cout&lt;&lt;\"Inna praca zosta\u0142a do tej pory wykonana: \"&lt;&lt;otherWork&lt;&lt;\" razy\"&lt;&lt;endl;\n        }\n        else {\n            cout&lt;&lt;\"W\u0105tek \"&lt;&lt;i&lt;&lt;\" oczekuje na wej\u015bcie. Tymczasem robi co\u015b innego \"&lt;&lt;endl;\n            std::this_thread::sleep_for(std::chrono::milliseconds(300));\n            actualThreadWork++;\n            cout&lt;&lt;\"Inna praca w\u0105tku: \"&lt;&lt;i&lt;&lt;\" zosta\u0142a wykonana: \"&lt;&lt;actualThreadWork&lt;&lt;\" razy\"&lt;&lt;endl;\n        }\n    }\n}\nint main()\n{\n    std::vector&lt;std::thread&gt; threads;\n    for(int i = 0;i&lt;THREAD_COUNT;i++) {\n        threads.push_back(std::thread(exampleFunc,i));\n    }\n    for_each(threads.begin(),threads.end(),[](std::thread&amp; v)-&gt;void{v.join();});\n    return 0;\n}\n\n<\/pre>\n<\/div>\n<pre class=\"EditorResult\" id=\"editor7Result\"><\/pre>\n<\/div>\n\n\n\n<p>Powy\u017cszy przyk\u0142ad prezentuje u\u017cycie strategii try_to_lock. Nasz\u0105 funkcj\u0105, kt\u00f3r\u0105 wykonuje ka\u017cdy z w\u0105tk\u00f3w jest exampleFunc. Gdy w\u0105tkowi uda si\u0119 uzyska\u0107 dost\u0119p na wy\u0142\u0105czno\u015b\u0107, modyfikowane s\u0105 globalne zmienne doneWork i otherWork. W przypadku, gdy wy\u0142\u0105czno\u015bci nie uda si\u0119 uzyska\u0107, w\u0105tek modyfikuje sobie lokalne zmienne. Zmiana lokalnych zmiennych jest bezpieczna, gdy\u017c nale\u017c\u0105 one tylko do danego w\u0105tku. Tym samym oszcz\u0119dzamy czas i mo\u017cemy przyspieszy\u0107 obliczenia, kt\u00f3re wykonuje nasz program. Oczywi\u015bcie, mimo zmiany strategii, unique_lock zachowuje swoj\u0105 g\u0142\u00f3wn\u0105 zalet\u0119 &#8211; zwalnia blokad\u0119 w momencie, gdy w\u0105tek opu\u015bci blok kodu funkcji. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Podsumowuj\u0105c<\/h2>\n\n\n\n<p>Pozna\u0142e\u015b dzisiaj du\u017co bardzo ciekawej i przydatnej wiedzy. Unique_lock i lock_guard znacznie u\u0142atwiaj\u0105 i przyspieszaj\u0105 tworzenie program\u00f3w wsp\u00f3\u0142bie\u017cnych. Dowiedzia\u0142e\u015b si\u0119 tak\u017ce o r\u00f3\u017cnych strategiach, kt\u00f3re pozwalaj\u0105 modyfikowa\u0107 zachowanie zar\u00f3wno unique_lock oraz lock_guard w taki spos\u00f3b, aby jak najlepiej wsp\u00f3\u0142pracowa\u0142y z twoim algorytmem. <\/p>\n\n\n\n<p>Pisz\u0105c swoje w\u0142asne programy pami\u0119taj o tym, aby jak najrzadziej (nigdy) nie u\u017cywa\u0107 go\u0142ych muteks\u00f3w. Je\u015bli tylko si\u0119 da, u\u017cywaj lock_guard, gdy\u017c jest on &#8222;najl\u017cejszy&#8221; i najszybszy. W pozosta\u0142ych wypadkach dobrym rozwi\u0105zaniem b\u0119dzie unique_lock. <\/p>\n\n\n\n<p>Je\u015bli masz jakie\u015b pytania czy uwagi, pisz \ud83d\ude42 W kolejnej cz\u0119\u015bci zajmiemy si\u0119 typowymi, omawianymi przez literatur\u0119 problemami synchronizacji w\u0105tk\u00f3w, czyli praktycznym podej\u015bciem do zdobytej dzisiaj wiedzy. <\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Czas czytania:<\/span> <span class=\"rt-time\">5<\/span> <span class=\"rt-label rt-postfix\">minut<\/span><\/span> W poprzedniej cz\u0119\u015bci dowiedzia\u0142e\u015b si\u0119 czym s\u0105 w\u0105tki oraz po co ich u\u017cywa\u0107. Pozna\u0142e\u015b tak\u017ce podstawow\u0105 metod\u0119 synchronizacji, kt\u00f3r\u0105 jest muteks, wprowadzony wraz z wersj\u0105 C++11. Wierz\u0119, \u017ce jest to &#8230;<\/p>\n","protected":false},"author":1,"featured_media":746,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[31],"tags":[48,52],"_links":{"self":[{"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/posts\/695"}],"collection":[{"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/comments?post=695"}],"version-history":[{"count":85,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/posts\/695\/revisions"}],"predecessor-version":[{"id":1492,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/posts\/695\/revisions\/1492"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/media\/746"}],"wp:attachment":[{"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/media?parent=695"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/categories?post=695"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/tags?post=695"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}