De-Vision.pl opinie

Synchronizacja wątków w C# i C++ (Spinlock)           

Wielowątkowość, synchronizacja… Ale po co to komu?

Przytoczę pewne bardzo obrazowe porównanie. Wyobraź sobie, że jesteś w restauracji. Podnosisz menu, wybierasz potrawę i czekasz na kelnera. Widzisz, że tymczasowo obsługuje innego klienta. Mija kilka, kilkanaście minut, a kelner wciąż stoi przy stoliku innego klienta, obsługuje tylko jego. Dopiero, gdy klient płaci i wychodzi z restauracji, kelner podchodzi do Ciebie i zaczyna cię obsługiwać. W tym czasie przychodzi inny klient, ale kelner jest zajęty tylko tobą. Mniej więcej tak działa aplikacja jednowątkowa (nasz kelner). W oczywisty sposób jest to nieefektywne.

Wtedy niczym superbohater, przybywa wielowątkowość.

Super! A więc zyskujemy trochę wydajności. Jednak rozważmy ten kod w języku C# i przeanalizujmy go.

using System;
using System.IO;
class Program
{
static void Main(string[] args)
{
string path = @”C:UserscamedDesktopexamplefile.txt”;                      if(!File.Exists(path))
{
File.Create(path);
}
}
}

Co tu się dzieje? Program sprawdza czy plik examplefile.txt istnieje, jeżeli nie, tworzy go. Teraz przejdźmy do tego, dlaczego kod ten nie jest bezpieczny. Jako, że dziś mamy do czynienia z wieloma procesorami (rdzeniami), to w tle mogą działać równocześnie różne programy. Nie ma więc żadnej gwarancji, że jeden z nich nie utworzy pliku o nazwie examplefile.txtwłaśnie w tym katalogu. Rozchodzi się więc o moment pomiędzy otrzymaniem informacji, że plik nie istnieje, a utworzeniem go. Klasa tego błędu to tzw. Race Condition. Aby uniknąć takich sytuacji, wymagana jest synchronizacja.

Najprostszym używanym niskopoziomowym mechanizmem do synchronizacji są tzw. Spinlocki (w luźnym tłumaczeniu – kręcące się blokady). Jest on w zasadzie aktywną pętlą, która czeka, aż dany obiekt (blokada) zostanie zwolniony. Dziś w zasadzie się z nich nie korzysta, gdyż nowoczesne języki takie jak C# mają wbudowane mechanizmy synchronizujące, takie jak lock. Lock działa w ten sposób:

object syncingObject = new object();
void thr()
{
lock(syncingObject)
{
// sekcja krytyczna
}
}

i jest równoważny temu:

object syncingObject = new object();
void thr()
{
System.Threading.Monitor.Enter(syncingObject);
try
{
// sekcja krytyczna
}
finally
{
System.Threading.Monitor.Exit(syncingObject);
}
}

gdyż lock jest w zasadzie aliasem tego drugiego. Teraz, zanim przejdę do spinlocków, wytłumaczę z pomocą Wikipedii czym jest owa sekcja krytyczna.

Sekcja krytyczna – fragment kodu programu, w którym korzysta się z zasobu dzielonego, a co za tym idzie w danej chwili może być wykorzystywany przez co najwyżej jeden wątek.

Źródło - https://nastoletni.pl/synchronizacja-watkow-w-c-i-c-spinlock/