{"id":23261,"date":"2023-08-08T05:00:00","date_gmt":"2023-08-08T03:00:00","guid":{"rendered":"https:\/\/sii.pl\/blog\/?p=23261"},"modified":"2024-07-22T14:47:38","modified_gmt":"2024-07-22T12:47:38","slug":"wspolbieznosc-w-go","status":"publish","type":"post","link":"https:\/\/sii.pl\/blog\/wspolbieznosc-w-go\/","title":{"rendered":"Wsp\u00f3\u0142bie\u017cno\u015b\u0107 w Go"},"content":{"rendered":"\n<p>Dawniej, kiedy w urz\u0105dzeniach by\u0142 tylko jeden w\u0105tek b\u0105d\u017a nawet nie by\u0142o system\u00f3w do zarz\u0105dzania procesami, kod wykonywa\u0142 si\u0119 linijka po linijce.&nbsp;<\/p>\n\n\n\n<p>W dzisiejszych czasach mamy ju\u017c lepsze komputery, kt\u00f3re cz\u0119sto posiadaj\u0105 co najmniej dwa w\u0105tki w procesorze. Dysponujemy r\u00f3wnie\u017c systemami do zarz\u0105dzania procesami. Dzi\u0119ki temu, jeste\u015bmy w stanie wykonywa\u0107 programy, kt\u00f3re s\u0105 o wiele bardziej z\u0142o\u017cone \u2013 takie, jak w\u0142a\u015bnie wsp\u00f3\u0142bie\u017cne programowanie. Oznacza to, \u017ce cz\u0119sto kod jest wykonywany r\u00f3wnocze\u015bnie, co mo\u017ce prowadzi\u0107 do znacznie szybszego przetwarzania danych. <\/p>\n\n\n\n<p>W tym celu Go udost\u0119pnia nam narz\u0119dzie takie jak gorutyny (ang. gorutines), kt\u00f3re przybli\u017c\u0119 w niniejszym artykule.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Czym s\u0105 gorutyny?<\/strong><\/h2>\n\n\n\n<p>Gorutyny to ,,lekki w\u0105tek\u201d, kt\u00f3ry wykonuje funkcje b\u0105d\u017a metody jednocze\u015bnie. Same gorutyny dzia\u0142aj\u0105 asynchronicznie. Je\u017celi ,,runtime\u201d ma wi\u0119cej przypisanych proces\u00f3w, to wtedy mog\u0105 dzia\u0142a\u0107 r\u00f3wnie\u017c r\u00f3wnolegle. Spos\u00f3b ich uruchomienia jest bardzo prosty. Trzeba zdefiniowa\u0107 funkcj\u0119 oraz przed jej wywo\u0142aniem napisa\u0107 po prostu \u201ego\u201d.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: go; title: ; notranslate\" title=\"\">\npackage main\n\nimport (\n\t&quot;fmt&quot;\n)\n\nfunc HelloWorld() {\n\tfmt.Println(&quot;Hello world!&quot;)\n}\n\nfunc main() {\n\tgo HelloWorld()\n}\n\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\"><strong>Synchronizacja gorutyn<\/strong><\/h2>\n\n\n\n<p>Mo\u017cna by pomy\u015ble\u0107: ,,wow, jakie to proste\u201d.&nbsp; Niestety \u2013 powy\u017cszy kod nie poka\u017ce nam nic. Wynika to z tego, \u017ce g\u0142\u00f3wna linia programu si\u0119 sko\u0144czy, zanim gorutyna zd\u0105\u017cy si\u0119 wykona\u0107.<\/p>\n\n\n\n<p>Aby zapobiec takiej sytuacji, nale\u017ca\u0142oby poczeka\u0107 w g\u0142\u00f3wnej linii programu na moment, a\u017c gorutyny zostan\u0105 wykonane.<\/p>\n\n\n\n<p>S\u0105 na to dwa sposoby:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>WaitGroups \u2013 mo\u017cna je przekaza\u0107 do funkcji lub opakowa\u0107 w funkcj\u0119 anonimow\u0105:<\/li>\n<\/ul>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: go; title: ; notranslate\" title=\"\">\npackage main\n\nimport (\n\t&quot;fmt&quot;\n\t&quot;sync&quot;\n)\n\nfunc worker(id int, wg *sync.WaitGroup) {\n\tdefer wg.Done()\n\n\tfmt.Printf(&quot;Worker %d started\\n&quot;, id)\n\n\t\/\/ Worker job\n\n\tfmt.Printf(&quot;Worker %d finished\\n&quot;, id)\n}\n\nfunc main() {\n\tvar wg sync.WaitGroup\n\n\t\/\/ Statring few worker gorutines\n\tfor i := 1; i &lt;= 5; i++ {\n\t\twg.Add(1) \/\/ Add number of gorutines called\n\t\tgo worker(i, &amp;wg)\n\t}\n\n\t\/\/ Waiting for worker finish job\n\twg.Wait()\n\n\tfmt.Println(&quot;All workers finished&quot;)\n}\n\n<\/pre><\/div>\n\n\n<p>Istnieje r\u00f3wnie\u017c mo\u017cliwo\u015b\u0107, aby t\u0119 funkcj\u0119 uruchomi\u0107 jako funkcj\u0119 anonimow\u0105. Dzi\u0119ki temu nie musimy pisa\u0107 specjalnej funkcji do tak prostych rzeczy.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Kana\u0142y (ang. chanells) \u2013 mo\u017cna je wykorzysta\u0107 do komunikacji pomi\u0119dzy dwiema r\u00f3\u017cnymi gorutynami lub tak, jak poka\u017c\u0119 to w przyk\u0142adzie, mo\u017cna z nich skorzysta\u0107 do komunikacji o tym, \u017ce dana gorutyna ju\u017c sko\u0144czy\u0142a.<\/li>\n<\/ul>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: go; title: ; notranslate\" title=\"\">\npackage main\n\nimport (\n\t&quot;fmt&quot;\n)\n\nfunc HelloWorld(done chan int) {\n\tfmt.Printf(&quot;Hello world!&quot;)\n\t\/\/ done &lt;- 1\n}\n\nfunc main() {\n\t\/\/ Definition of the chanel\n\tdone := make(chan int)\n\n\t\/\/ starting of goroutine\n\tgo HelloWorld(done)\n\n\t\/\/ waiting for finish\n\t&lt;-done\n}\n\n<\/pre><\/div>\n\n\n<p>W tym przyk\u0142adzie po wykonaniu funkcji HelloWorld w kana\u0142 done zostanie przes\u0142ana liczba jeden. Natomiast w g\u0142\u00f3wnej linii programu oczekujemy na sygna\u0142 &lt;-done.&nbsp;<\/p>\n\n\n\n<p>Jak wida\u0107, obie metody s\u0105 poprawne, jednak metoda z wait grupami jest bardziej czytelna. Do wi\u0119kszej liczby gorutyn powinni\u015bmy u\u017cywa\u0107 wait group, z racji tego, \u017ce przyk\u0142ad z kana\u0142em obs\u0142u\u017cy nam tylko i wy\u0142\u0105cznie jedn\u0105 gorutyn\u0119.<\/p>\n\n\n\n<p>Podczas korzystania z obu metod trzeba pami\u0119ta\u0107 o tym, \u017ceby je prawid\u0142owo zamkn\u0105\u0107. Gdy tego nie zrobimy, dojdzie do sytuacji, kt\u00f3rej ka\u017cdy programista si\u0119 obawia \u2013 nast\u0105pi deadlock.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Jak dzia\u0142aj\u0105 gorutyny?<\/strong><\/h2>\n\n\n\n<p>J\u0119zyk Go sam zarz\u0105dza tym, jak gorutyny s\u0105 wykonywane. M\u00f3wimy, \u017ce jest to wykonywanie jednoczesne, ale w rzeczywisto\u015bci tak by\u0107 nie musi.&nbsp;Gorutynami zarz\u0105dza Scheduler Go. Dzia\u0142a on w tle i automatycznie decyduje, czy dan\u0105 gorutyn\u0119 wykona\u0107 sekwencyjnie, czy przypisa\u0107 j\u0105 do w\u0105tku.&nbsp;<\/p>\n\n\n\n<p>Warto zaznaczy\u0107, \u017ce <strong>programista Go nie ma kontroli<\/strong> nad tym, czy dana gorutyna zostanie wykonana w osobnym w\u0105tku, czy te\u017c zostanie wykonana sekwencyjnie. <\/p>\n\n\n\n<p>Gorutyny zajmuj\u0105 bardzo ma\u0142o pami\u0119ci \u2013 zazwyczaj 2-4 kB. Oczywi\u015bcie, ta waga mo\u017ce by\u0107 wi\u0119ksza \u2013 zale\u017cy to od wielko\u015bci stosu, ilo\u015bci pami\u0119ci zaalokowanej na stercie oraz liczby zmiennych lokalnych, a tak\u017ce liczby u\u017cywanych kana\u0142\u00f3w i mutex\u00f3w.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Wykonanie sekwencyjne a przypisanie do w\u0105tku<\/strong><\/h3>\n\n\n\n<p>Co to znaczy, \u017ce gorutyna jest wykonana sekwencyjnie, a co, \u017ce przypisana do w\u0105tku? Do wyt\u0142umaczenia zarz\u0105dzania wykonaniem sekwencyjnym za\u0142o\u017cymy, \u017ce procesor ma tylko jeden w\u0105tek.<\/p>\n\n\n\n<p>Scheduler Go b\u0119dzie zarz\u0105dza\u0142 dost\u0119pem czasu do procesora tak, aby ka\u017cdej gorutynie przydzieli\u0107 go r\u00f3wnomiernie. &nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/08\/Obraz1.png\"><img decoding=\"async\" width=\"589\" height=\"134\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/08\/Obraz1.png\" alt=\"Zarz\u0105dzanie czasem w Scheduler Go\" class=\"wp-image-23263\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/08\/Obraz1.png 589w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/08\/Obraz1-300x68.png 300w\" sizes=\"(max-width: 589px) 100vw, 589px\" \/><\/a><figcaption class=\"wp-element-caption\">Ryc. 1 Zarz\u0105dzanie czasem w Scheduler Go<\/figcaption><\/figure>\n\n\n\n<p>Je\u017celi gorutyny b\u0119d\u0105 dzia\u0142a\u0142y na jednym w\u0105tku, to nie ma gwarancji, \u017ce zostan\u0105 one uruchamiane w konkretnej kolejno\u015bci. Scheduler zdecyduje, kt\u00f3r\u0105 z nich uruchomi\u0107 w danym momencie. Mo\u017ce to wynika\u0107 z dost\u0119pnych zasob\u00f3w takich jak np. kana\u0142y czy mutexy. Og\u00f3lna kolejno\u015b\u0107 wykonywania jest zale\u017cna od ich gotowo\u015bci oraz momentu dodania do kolejki.<\/p>\n\n\n\n<p>Natomiast, je\u017celi mamy procesor, kt\u00f3ry ma co najmniej dwa w\u0105tki, gorutyny mog\u0105 zosta\u0107 wykonane r\u00f3wnolegle na r\u00f3\u017cnych w\u0105tkach.&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/08\/Obraz2.png\"><img decoding=\"async\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/08\/Obraz2.png\" alt=\"R\u00f3wnoleg\u0142e wykonanie gorutyn\" class=\"wp-image-23265\" width=\"594\" height=\"343\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/08\/Obraz2.png 594w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/08\/Obraz2-300x173.png 300w\" sizes=\"(max-width: 594px) 100vw, 594px\" \/><\/a><figcaption class=\"wp-element-caption\">Ryc. 2 R\u00f3wnoleg\u0142e wykonanie gorutyn<\/figcaption><\/figure>\n\n\n\n<p>Podsumowuj\u0105c: gorutyny 3 i 1 zostan\u0105 wykonane dok\u0142adnie w tym samym czasie. Natomiast gorutyny 2 i 4, zostan\u0105 wykonane r\u00f3wnolegle, gdy zostanie im przydzielony czas.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Problemy gorutyn<\/strong><\/h2>\n\n\n\n<p>Gorutyny s\u0105 dosy\u0107 proste i intuicyjne w wykorzystaniu. Trzeba jednak pami\u0119ta\u0107, \u017ce jest to ca\u0142y czas programowanie wsp\u00f3\u0142bie\u017cne, kt\u00f3re mo\u017ce prowadzi\u0107 do takich samych problem\u00f3w jak w programowaniu na w\u0105tkach. Nale\u017c\u0105 do nich:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>race condition,<\/li>\n\n\n\n<li>deadlock,<\/li>\n\n\n\n<li>starvation.<\/li>\n<\/ul>\n\n\n\n<p>Mo\u017cemy r\u00f3wnie\u017c napotka\u0107:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>tzw. live-locki,<\/li>\n\n\n\n<li>wyciek gorutyny.&nbsp;<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Deadlock<\/strong><\/h2>\n\n\n\n<p>Deadlock wyst\u0119puje, gdy gorutyny czekaj\u0105 na siebie nawzajem, wzajemnie si\u0119 blokuj\u0105c. W takim wypadku program nigdy si\u0119 nie zako\u0144czy.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: go; title: ; notranslate\" title=\"\">\npackage main\n\nimport (\n\t&quot;fmt&quot;\n\t&quot;time&quot;\n)\n\nfunc main() {\n\tch1 := make(chan int)\n\tch2 := make(chan int)\n\n\tgo func() {\n\t\tch1 &lt;- 1\n\n\t\tv := &lt;-ch2\n\t\tfmt.Println(v)\n\t}()\n\n\tgo func() {\n\t\tch2 &lt;- 2\n\n\t\tv := &lt;-ch1\n\t\tfmt.Println(v)\n\t}()\n\n\t\/\/ deadlock!\n\ttime.Sleep(5 * time.Second)\n}\n\n<\/pre><\/div>\n\n\n<p>W powy\u017cszym przyk\u0142adzie mamy dwie gorutyny, kt\u00f3re komunikuj\u0105 si\u0119 za pomoc\u0105 dw\u00f3ch kana\u0142\u00f3w: ch1 i ch2. Pierwsza gorutyna pr\u00f3buje przes\u0142a\u0107 warto\u015b\u0107 1 do kana\u0142u ch1, a nast\u0119pnie usi\u0142uje odebra\u0107 warto\u015b\u0107 z kana\u0142u ch2. Druga gorutyna pr\u00f3buje przes\u0142a\u0107 warto\u015b\u0107 2 do kana\u0142u ch2, a nast\u0119pnie odebra\u0107 warto\u015b\u0107 z kana\u0142u ch1.<\/p>\n\n\n\n<p>Jednak\u017ce, obie gorutyny czekaj\u0105 wzajemnie na przes\u0142anie danych przez kana\u0142y, co prowadzi do deadlocka. Program zawiesza si\u0119 i nie ko\u0144czy. Aby unikn\u0105\u0107 deadlock\u00f3w w gorutynach, nale\u017cy unika\u0107 sytuacji, w kt\u00f3rych dwie gorutyny (lub wi\u0119cej) czekaj\u0105 na siebie nawzajem. Mo\u017cna to osi\u0105gn\u0105\u0107 poprzez u\u017cycie jednego kana\u0142u, kt\u00f3ry dzia\u0142a w dw\u00f3ch kierunkach lub poprzez u\u017cycie mechanizmu synchronizacji takiego jak WaitGroup lub Mutex.<\/p>\n\n\n\n<p>Poni\u017cej zaprezentuj\u0119 w\u0142a\u015bciw\u0105 implementacj\u0119 tak, aby taka sytuacja si\u0119 nie pojawi\u0142a.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: go; title: ; notranslate\" title=\"\">\npackage main\n\nimport (\n    &quot;fmt&quot;\n    &quot;sync&quot;\n)\n\nfunc main() {\n    ch := make(chan int)\n    var wg sync.WaitGroup\n    wg.Add(2)\n\n    go func() {\n        ch &lt;- 1\n        val := &lt;-ch\n        fmt.Println(val)\n        wg.Done()\n    }()\n\n    go func() {\n        val := &lt;-ch\n        fmt.Println(val)\n        ch &lt;- 2\n        wg.Done()\n    }()\n\n    wg.Wait()\n}\n\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\"><strong>Race-condition<\/strong><\/h2>\n\n\n\n<p>Gdy gorutyny maj\u0105 dost\u0119p do tych samych danych bez mutex\u00f3w s\u0142u\u017c\u0105cych do ich synchronizacji, mo\u017ce pojawi\u0107 si\u0119 w\u0142a\u015bnie ta sytuacja. Dzieje si\u0119 tak, kiedy dwie lub wi\u0119cej gorutyn pr\u00f3buje zapisa\u0107 warto\u015b\u0107 do tej samej zmiennej. Wynik takiej sytuacji jest nie do przewidzenia. Natomiast jasne jest to, \u017ce dane b\u0119d\u0105 niesp\u00f3jne.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: go; title: ; notranslate\" title=\"\">\npackage main\n\nimport (\n\t&quot;fmt&quot;\n\t&quot;sync&quot;\n)\n\nfunc main() {\n\tvar wg sync.WaitGroup\n\twg.Add(2)\n\n\tvar counter int\n\n\tgo func() {\n\t\tfor i := 0; i &lt; 10000; i++ {\n\t\t\tcounter++\n\t\t}\n\t\twg.Done()\n\t}()\n\n\tgo func() {\n\t\tfor i := 0; i &lt; 10000; i++ {\n\t\t\tcounter++\n\t\t}\n\t\twg.Done()\n\t}()\n\n\twg.Wait()\n\tfmt.Println(&quot;Counter:&quot;, counter)\n}\n\n<\/pre><\/div>\n\n\n<p>Aby zapobiec takiej sytuacji, nale\u017cy u\u017cy\u0107 mutexa:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: go; title: ; notranslate\" title=\"\">\npackage main\n\nimport (\n&quot;fmt&quot;\n&quot;sync&quot;\n)\n\nvar (\ncounter = 0\nmutex = &amp;sync.Mutex{}\n)\n\nfunc main() {\nvar wg sync.WaitGroup\nfor i := 0; i &lt; 100; i++ {\nwg.Add(1)\ngo incrementCounter(&amp;wg)\n}\nwg.Wait()\nfmt.Println(&quot;Counter:&quot;, counter)\n}\n\nfunc incrementCounter(wg *sync.WaitGroup) {\nmutex.Lock()\ndefer mutex.Unlock()\ncounter++\nwg.Done()\n}\n<\/pre><\/div>\n\n\n<p>Wynikiem tego kodu b\u0119dzie 100, dzi\u0119ki temu, \u017ce zastosowali\u015bmy mutexa, aby zsynchronizowa\u0107 czas dost\u0119pu do zmiennej counter.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Starvation<\/strong><\/h2>\n\n\n\n<p>Starvation to sytuacja, w kt\u00f3rej pewne zadania nie s\u0105 w stanie wykona\u0107 swojej pracy, poniewa\u017c s\u0105 stale zablokowane lub nie otrzymuj\u0105 wystarczaj\u0105cej ilo\u015bci zasob\u00f3w, kt\u00f3rych potrzebuj\u0105 do pracy. W efekcie te zadania s\u0105 niesko\u0144czenie odk\u0142adane w czasie, co prowadzi do nieefektywno\u015bci i braku post\u0119pu w wykonywaniu programu.<\/p>\n\n\n\n<p>Przyk\u0142adem mo\u017ce by\u0107 sytuacja, w kt\u00f3rej wiele gorutyn oczekuje na dost\u0119p do jednego zasobu (np. blokuj\u0105cego mutexa). Je\u015bli jedna z gorutyn jest w stanie zaj\u0105\u0107 ten zas\u00f3b na d\u0142u\u017cszy czas, to inne gorutyny mog\u0105 by\u0107 stale blokowane, co prowadzi do ich \u201eg\u0142odzenia\u201d i braku mo\u017cliwo\u015bci wykonania swojej pracy.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: go; title: ; notranslate\" title=\"\">\npackage main\n\nimport (\n\t&quot;fmt&quot;\n\t&quot;sync&quot;\n\t&quot;time&quot;\n)\n\nvar mutex sync.Mutex\n\nfunc main() {\n\twg := sync.WaitGroup{}\n\twg.Add(2)\n\n\t\/\/ Gorutyna 1\n\tgo func() {\n\t\tdefer wg.Done()\n\n\t\tmutex.Lock()\n\t\tdefer mutex.Unlock()\n\n\t\tfmt.Println(&quot;Goroutine 1 has acquired the lock&quot;)\n\t\ttime.Sleep(10 * time.Second)\n\t}()\n\n\t\/\/ Gorutyna 2\n\tgo func() {\n\t\tdefer wg.Done()\n\n\t\tmutex.Lock()\n\t\tdefer mutex.Unlock()\n\n\t\tfmt.Println(&quot;Goroutine 2 has acquired the lock&quot;)\n\t}()\n\n\twg.Wait()\n}\n<\/pre><\/div>\n\n\n<p>Aby zapobiec takiej sytuacji jak wy\u017cej, musieliby\u015bmy zaimplementowa\u0107 kolejki i semafory:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: go; title: ; notranslate\" title=\"\">\npackage main\n\nimport (\n\t&quot;fmt&quot;\n\t&quot;sync&quot;\n\t&quot;time&quot;\n)\n\nconst maxConcurrent = 1\n\nvar (\n\tmutex       = sync.Mutex{}\n\tjobDuration = time.Millisecond * 500\n)\n\nfunc main() {\n\twg := sync.WaitGroup{}\n\tsemaphore := make(chan struct{}, maxConcurrent)\n\n\tfor i := 0; i &lt; 10; i++ {\n\t\twg.Add(1)\n\t\tgo func(i int) {\n\t\t\tdefer wg.Done()\n\n\t\t\tsemaphore &lt;- struct{}{}\n\n\t\t\tfmt.Printf(&quot;Goroutine %d: Entering critical section.\\n&quot;, i)\n\n\t\t\tmutex.Lock()\n\n\t\t\tfmt.Printf(&quot;Goroutine %d: Executing critical section.\\n&quot;, i)\n\t\t\ttime.Sleep(jobDuration)\n\t\t\tfmt.Printf(&quot;Goroutine %d: Finished executing critical section.\\n&quot;, i)\n\n\t\t\tmutex.Unlock()\n\n\t\t\t&lt;-semaphore\n\t\t}(i)\n\t}\n\n\twg.Wait()\n\tfmt.Println(&quot;All goroutines have finished.&quot;)\n}\n<\/pre><\/div>\n\n\n<p>W powy\u017cszym przyk\u0142adzie zosta\u0142 stworzony semafor, kt\u00f3ry m\u00f3wi, ile gorutyn mo\u017ce si\u0119 znajdowa\u0107 w danej chwili w sekcji krytycznej. <\/p>\n\n\n\n<p>Dzia\u0142a to w ten spos\u00f3b, \u017ce gorutyna pr\u00f3buje przes\u0142a\u0107 pust\u0105 struktur\u0119 do kana\u0142u. Nie jest to jednak mo\u017cliwe, gdy\u017c jest to kana\u0142 buforowany dopuszczaj\u0105cy tylko jeden element w danej chwili.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Live-Lock<\/strong><\/h2>\n\n\n\n<p>Live-lock jest sytuacj\u0105, w kt\u00f3rej gorutyny wykonuj\u0105 nieko\u0144cz\u0105cy si\u0119 cykl zale\u017cno\u015bci, co powoduje, \u017ce nie s\u0105 w stanie kontynuowa\u0107 pracy. Mo\u017ce to prowadzi\u0107 do sytuacji, w kt\u00f3rej program ca\u0142y czas dzia\u0142a, ale niczego nie osi\u0105ga:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: go; title: ; notranslate\" title=\"\">\npackage main\n\nimport (\n\t&quot;fmt&quot;\n\t&quot;sync&quot;\n)\n\nvar lockA sync.Mutex\nvar lockB sync.Mutex\n\nfunc goroutineA() {\n\tfor {\n\t\tlockA.Lock()\n\t\tlockB.Lock()\n\t\tfmt.Println(&quot;Goroutine A&quot;)\n\t\tlockA.Unlock()\n\t\tlockB.Unlock()\n\t}\n}\n\nfunc goroutineB() {\n\tfor {\n\t\tlockB.Lock()\n\t\tlockA.Lock()\n\t\tfmt.Println(&quot;Goroutine B&quot;)\n\t\tlockB.Unlock()\n\t\tlockA.Unlock()\n\t}\n}\n\nfunc main() {\n\tgo goroutineA()\n\tgo goroutineB()\n\tfor {\n\t}\n}\n\n<\/pre><\/div>\n\n\n<p>W powy\u017cszym przyk\u0142adzie mamy dwie gorutyny (goroutineA i goroutineB), kt\u00f3re wykonuj\u0105 nieko\u0144cz\u0105cy si\u0119 cykl zale\u017cno\u015bci. Obie pr\u00f3buj\u0105 uzyska\u0107 blokad\u0119 na dw\u00f3ch r\u00f3\u017cnych zasobach, ale w takiej samej kolejno\u015bci. W efekcie ka\u017cda gorutyna musi czeka\u0107 na zwolnienie blokady przez drug\u0105, co prowadzi do sytuacji, w kt\u00f3rej obie gorutyny zawiesz\u0105 si\u0119 w niesko\u0144czono\u015b\u0107 i nie b\u0119d\u0105 w stanie kontynuowa\u0107 pracy.<\/p>\n\n\n\n<p>Nie ma jednoznacznej recepty na to, \u017ceby unikn\u0105\u0107 tego problemu \u2013 trzeba odpowiednio przemy\u015ble\u0107 sw\u00f3j algorytm, aby taka sytuacja nie wyst\u0105pi\u0142a.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Goroutine leakage<\/strong><\/h2>\n\n\n\n<p>Do Goroutine leakage dochodzi wtedy, gdy gorutyna jest niepoprawnie zamykana i nie jest zwalniana z pami\u0119ci.&nbsp;<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: go; title: ; notranslate\" title=\"\">\npackage main\n\nimport (\n\t&quot;fmt&quot;\n\t&quot;time&quot;\n)\n\nfunc main() {\n\tgo leakyFunction()\n\tfmt.Println(&quot;Main function is exiting.&quot;)\n}\n\nfunc leakyFunction() {\n\tfor {\n\t\ttime.Sleep(time.Second)\n\t}\n}\n<\/pre><\/div>\n\n\n<p>Funkcja leakyFunction jest niesko\u0144czona, gdy ju\u017c g\u0142\u00f3wna linia progamu jest zako\u0144czona. Prowadzi to do wycieku.<\/p>\n\n\n\n<p>Aby zapobiec takiej sytuacji, mo\u017cna skorzysta\u0107 z contextu i przekazywa\u0107 go do gorutyn. Je\u017celi zdarzy si\u0119 sytuacja, \u017ce program g\u0142\u00f3wny b\u0119dzie zako\u0144czony, to wtedy gorutyny r\u00f3wnie\u017c b\u0119d\u0105 zamkni\u0119te.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: go; title: ; notranslate\" title=\"\">\npackage main\n\nimport (\n\t&quot;context&quot;\n\t&quot;fmt&quot;\n\t&quot;sync&quot;\n\t&quot;time&quot;\n)\n\nfunc goroutineCancel(cancel context.CancelFunc) {\n\ttime.Sleep(3 * time. Second)\n\tcancel()\n}\n\nfunc main() {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\twg := &amp;sync.WaitGroup{}\n\tgo goroutineCancel(cancel)\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase &lt;-ctx.Done():\n\t\t\t\tfmt.Println(&quot;goroutine canceled&quot;)\n\t\t\t\treturn\n\t\t\tdefault:\n\t\t\t\tfmt.Println(&quot;goroutine running&quot;)\n\t\t\t\ttime.Sleep(time.Second)\n\t\t\t}\n\t\t}\n\t}()\n\n\twg.Wait()\n\tfmt.Println(&quot;all goroutines completed&quot;)\n}\n<\/pre><\/div>\n\n\n<p>W tym przyk\u0142adzie mamy niesko\u0144czon\u0105 gorutyn\u0119, kt\u00f3ra wykonuje si\u0119 w p\u0119tli i wypisuje na ekranie \u201egoroutine running\u201d co sekund\u0119. Aby zabezpieczy\u0107 si\u0119 przed wyciekiem gorutyny, dodajemy j\u0105 do WaitGroup i oczekujemy na jej zako\u0144czenie za pomoc\u0105 wg.Wait().<\/p>\n\n\n\n<p>\u017beby anulowa\u0107 gorutyn\u0119, gdy program zostanie zako\u0144czony, u\u017cywamy kontekstu ctx i wywo\u0142ujemy funkcj\u0119 cancel() w funkcji gorutineCancel. W przypadku anulowania kontekstu, gorutyna zostanie zako\u0144czona poprzez zako\u0144czenie p\u0119tli i wyj\u015bcie z funkcji.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Podsumowanie<\/strong><\/h2>\n\n\n\n<p>Jak wida\u0107, w por\u00f3wnaniu do innych j\u0119zyk\u00f3w programowania, gorutyny s\u0105 du\u017co prostsze w stosowaniu. Zw\u0142aszcza, \u017ce Go dostarcza nam ogromn\u0105 liczb\u0119 metod do tego, aby je synchronizowa\u0107 oraz \u017ceby komunikowa\u0107 si\u0119 mi\u0119dzy nimi.&nbsp;<\/p>\n\n\n\n<p>Nale\u017cy r\u00f3wnie\u017c pami\u0119ta\u0107, \u017ce gorutyny nie s\u0105 lekarstwem na wszystko. Korzystanie z nich mo\u017ce znacznie usprawni\u0107 i przyspieszy\u0107 dzia\u0142anie naszego programu, lecz wi\u0105\u017ce si\u0119 to z konsekwencjami w postaci trudniejszego testowania oraz znacznym podniesieniem z\u0142o\u017cono\u015bci kodu, gdy mamy sporo danych do synchronizowania pomi\u0119dzy gorutynami.<\/p>\n\n\n\n<p>***<\/p>\n\n\n\n<p>Je\u015bli interesuje Ci\u0119 obszar korutyn, zach\u0119camy r\u00f3wnie\u017c do zapoznania <a href=\"https:\/\/sii.pl\/blog\/wyszukiwarka\/korutyny\/\" target=\"_blank\" aria-label=\"z innymi artyku\u0142ami naszych ekspert\u00f3w.  (opens in a new tab)\" rel=\"noreferrer noopener\" class=\"ek-link\">z innymi artyku\u0142ami naszych ekspert\u00f3w. <\/a><\/p>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;23261&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;5&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;5&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;11&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;5\\\/5 ( votes: 5)&quot;,&quot;size&quot;:&quot;18&quot;,&quot;title&quot;:&quot;Wsp\u00f3\u0142bie\u017cno\u015b\u0107 w Go&quot;,&quot;width&quot;:&quot;139.5&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} ( {votes}: {count})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 139.5px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 14.4px;\">\n            5\/5 ( votes: 5)    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>Dawniej, kiedy w urz\u0105dzeniach by\u0142 tylko jeden w\u0105tek b\u0105d\u017a nawet nie by\u0142o system\u00f3w do zarz\u0105dzania procesami, kod wykonywa\u0142 si\u0119 linijka &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.pl\/blog\/wspolbieznosc-w-go\/\">Continued<\/a><\/p>\n","protected":false},"author":549,"featured_media":23272,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1314],"tags":[2427,1765,1764,1554],"class_list":["post-23261","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development-na-twardo","tag-digital","tag-gorutyny","tag-go","tag-zalety-i-wady"],"acf":[],"aioseo_notices":[],"republish_history":[],"featured_media_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2023\/08\/Wspolbieznosc-w-Go.jpg","category_names":["Development na twardo"],"_links":{"self":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/23261"}],"collection":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/users\/549"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/comments?post=23261"}],"version-history":[{"count":3,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/23261\/revisions"}],"predecessor-version":[{"id":23279,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/23261\/revisions\/23279"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media\/23272"}],"wp:attachment":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media?parent=23261"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/categories?post=23261"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/tags?post=23261"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}