Czy to są semafory?

0

Piszę pewien program pracujący w kilku wątkach. Są pewne dwie procedury, z pewnych względów nie można dopuścić do równoczesnego wykonywania się obu procedur, ponieważ operują one na zmiennych globalnych, każda zawiera kilka instrukcji operujących na tej samej strukturze, że wykonanie obu procedur naraz może zakłócić prawidłowe ich wykonywanie.

Celem przedstawionej konstrukcji jest uniemożliwienie wykonywania obu procedur naraz.
Poniższy przykład jest w C#, ale sprawa dotyczy kazdego języka obsługującego wielowątkowość.

// Zmienne globalne widoczne ze wszystkich wątków

bool Praca1 = false;
bool Praca2 = false;

// Wariant 1 - procedura jest wykonywana tylko wtedy, gdy można ją wykonać

public void Procedura1()
{
 if (!Praca2)
 {
  Praca1 = true;
  // jakies instrukcje
  Praca1 = false;
 }
}

public void Procedura2()
{
 if (!Praca1)
 {
  Praca2 = true;
  // jakies instrukcje
  Praca2 = false;
 }
}

// Wariant 2 - w przypadku niemożności wykonania procedury, oczekuje się na możliwość jej wykonania

public void Procedura1()
{
 Praca1 = true;
 while(Praca2)
 {
 }
 // jakies instrukcje
 Praca1 = false;
}

public void Procedura2()
{
 Praca2 = true;
 while(Praca1)
 {
 }
 // jakies instrukcje
 Praca2 = false;
}

Czy zmienne "Praca1" i "Praca2" to są semafory?

0

Opcja 1 to żadne semafory i w ogóle nie działa. Wyobraź sobie sytuację że wątek przechodzi za warunek if'a i następnie sterowanie zostaje przekazane do drugiego wątku który też przechodzi za swojego if'a (bo ta zmienna która miała to blokować jeszcze nie jest ustawiona) i bum bo obie metody mogą wykonywać instrukcje w tym samym czasie.
Opcja 2 to są tzw semafory z aktywnym czekaniem, dość zasobożerne i niepraktyczne.
Po co tak kombinować skoro praktycznie każdy język ma wbudowane mutexy, semafory i kupę innych mechanizmów do obsługi współbieżności (a jak nie ma to są do niego biblioteki które te funkcjonalności dodają)

0
Shalom napisał(a)

Opcja 1 to żadne semafory i w ogóle nie działa. Wyobraź sobie sytuację że wątek przechodzi za warunek if'a i następnie sterowanie zostaje przekazane do drugiego wątku który też przechodzi za swojego if'a (bo ta zmienna która miała to blokować jeszcze nie jest ustawiona) i bum bo obie metody mogą wykonywać instrukcje w tym samym czasie.

Napisałem kod, który uwzględnia ewentualność, o której piszesz.

bool Praca1 = false;
bool Praca10 = false;
bool Praca2 = false;
bool Praca20 = false;

public void Procedura1()
{
 Praca10 = true;
 if ((!Praca2) && (!Praca20))
 {
  Praca1 = true;
  // jakies instrukcje
  Praca1 = false;
 }
 Praca10 = false;
}
 
public void Procedura2()
{
 Praca20 = true;
 if ((!Praca1) && (!Praca10))
 {
  Praca2 = true;
  // jakies instrukcje
  Praca2 = false;
 }
 Praca20 = false;
}

Czy teraz to będzie dobrze działać? W dowolnym momencie uruchamiania drugiej procedury, nawet bezpośrednio za "if", zmienna z 0 w nazwie zablokuje jej uruchomienie.

Tutaj to samo prościej, ale nie jestem pewny, czy będzie działać:

bool Praca10 = false;
bool Praca20 = false;

public void Procedura1()
{
 Praca10 = true;
 if (!Praca20)
 {
  // jakies instrukcje
 }
 Praca10 = false;
}
 
public void Procedura2()
{
 Praca20 = true;
 if (!Praca10)
 {
  // jakies instrukcje
 }
 Praca20 = false;
}

Patrzyłem do wikipedii, tam są opisane tylko i wyłącznie semafory z aktywnym czekaniem. Mi chodzi również o zrealizowanie procedury, przez którą się przechodzi, tylko możliwość wykonania operacji na wspólnym zasobie określa, że operacja będzie wykonana lub pominięta, nie będzie oczekiwania, aż zasób się zwolni, jak jest on zajęty.

Shalom napisał(a)

Po co tak kombinować skoro praktycznie każdy język ma wbudowane mutexy, semafory i kupę innych mechanizmów do obsługi współbieżności (a jak nie ma to są do niego biblioteki które te funkcjonalności dodają)

Ja programuję w C#. Ja pierwszy raz spotykam się z taką sytuacją, moim zdaniem, wymyśliłem najprostszy sposób ominięcia wykonania procedury, która nie może być wykonana.

0

Wyobraź sobie sytuację że wątek przechodzi za warunek if'a i następnie sterowanie zostaje przekazane do drugiego wątku który też przechodzi za swojego if'a
jest to zdarzenie ogromnie nieprawdopodobne, ale ponieważ procesor pracuje z prędkością kilku miliardów cykli na sekundę, w końcu się trafi.
sytuacja robi się gorsza na procesorach wielordzeniowych: tam dwa wątki mogą się naprawdę „kręcić” jednocześnie, i prawdpodobieństwo takiego błędu znacznie wzrasta.
jako przykład dam Tomb Raider I, który z takiej lub podobnej przyczyny wywala się na dual core, a pomaga ograniczenie procesu do jednego jądra.

Jak Shalom powiedział, nie należy kombinować tylko użyć mechanizmów dostępnych w języku lub API systemu operacyjnego.

Ja pierwszy raz spotykam się z taką sytuacją, moim zdaniem, wymyśliłem najprostszy sposób ominięcia wykonania procedury, która nie może być wykonana.
Być może twoje rozwiązanie działa. Być może jednak wywala się raz na tysiąc uruchomień – to bardzo trudno zbadać. Być może nawet działa teraz, a przestanie na nowszym systemie operacyjnym lub nowej rodzinie procków.
A nawet jeśli to działa, przyzwyczaisz się że tak można robić, i za którymś razem okoliczności się minimalnie zmienią, i przestanie działać.
Poleganie na niepewnym lub niezdefiniowanym zachowaniu to zło.

Pisząc kod najlepiej się zastanowić: czy użyłbym tej konstrukcji pisząc soft do respiratora lub rakiety kosmicznej?

0

Brawo @andrzejlisek, ale twoje poprawione kody też nie działają, jak juz koledzy zauważyli ;] Przecież moze tak być że obie zmienne w dwoch wątkach ustawią sie na true i z dwóch potencjalnych wywołań metody w żadnej nie wykonaja sie instrukcje. Zamiana deadlock'a na niewykonanie kodu, brawo ;]
Na prawdę warto najpierw (!) poczytać troche na temat systemów operacyjnych, współbieżności, wielowątkowości i obliczeń równoległych a potem zabierać sie na wymyślanie rewolucyjnych rozwiązań. Na prawdę wielu łebskich ludzi nad tym myślało i warto z ich wiedzy korzystać ;)

0
Shalom napisał(a)

Brawo @andrzejlisek, ale twoje poprawione kody też nie działają, jak juz koledzy zauważyli ;] Przecież moze tak być że obie zmienne w dwoch wątkach ustawią sie na true i z dwóch potencjalnych wywołań metody w żadnej nie wykonaja sie instrukcje. Zamiana deadlock'a na niewykonanie kodu, brawo ;]
Na prawdę warto najpierw (!) poczytać troche na temat systemów operacyjnych, współbieżności, wielowątkowości i obliczeń równoległych a potem zabierać sie na wymyślanie rewolucyjnych rozwiązań. Na prawdę wielu łebskich ludzi nad tym myślało i warto z ich wiedzy korzystać ;)

Rozumiem, że mówisz o takiej sytuacji:

//--- WĄTEK 1 ---                --- WĄTEK 2 ---

public void Procedura1()
{
 Praca10 = true;
                               public void Procedura2()
                               {
                                Praca20 = true;
// --- MOMENT, W KTORYM OBIE ZMIENNE SA NA TRUE ---

 if (!Praca20) <- niespelnione
 {
                                if (!Praca10) <- tu tez niespelnione
                                {
  // jakies instrukcje
                                // jakies instrukcje
 }
 Praca10 = false;
                                }
                                Praca20 = false;
                                }
}

Oczywiście o sekcjach krytycznych w C# musze poczytać, bo wiem, że takie coś jest, ale nie wiem jeszcze, jak to się robi.
Jeżeli by założyć, że sytuacja, w której obie procedury są pominięte jest dopuszczalna, to rozwiązanie wydaje się być prawidłowe biorąc pod uwagę, że nawet na czterordzeniowcu prawdopodobieństwo takiej sytuacji jest bardzo małe, a w założeniach programu, autentyczne pominięcie obu procedur jednocześnie raz na 1000 wywołań jest dopuszczalne. Prawda?

0

Nie takie podejście nie jest dopuszczalne, ponieważ może się tak złożyć, że te procedury nigdy się nie wykonają. Poza tym sytuacja w której jakaś procedura może się nie wykonać powinna być jasno zdefiniowana.

3

I tak powstają WTFy. A potem się godzinami debuguje szukając przyczyny dla której program raz działa tak, raz inaczej, a raz się całkiem wywala.

0

Znalazłem to:

http://www.pzielinski.com/?p=176

Wypróbowałem System.Threading.Monitor.Enter i TryEnter i działa idealnie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1