2016-11-29 11 views
5

Tak więc dla zadania musimy mieć możliwość skorzystania z C# -Lock lub użyć samouruchomionego TaS-Lock. To, co przeczytałem na temat TaS-Locks, polega na tym, że używa 1 kroku atomowego do czytania i pisania wartości. Zasugerowano nam, że używamy klasy Interlocked w C# do tego.Jak mogę wdrożyć własną blokadę TaS w języku C#?

Jak dotąd jest to, co mam, ale wydaje się, aby doprowadzić do niespójnych odpowiedzi:

public interface Lock 
{ 
    void Lock(); 
    void Unlock(); 
} 

public class C_Sharp_Lock : Lock 
{ 
    readonly Object myLock = new object(); 

    public void Lock() 
    { 
     Monitor.Enter(myLock); 
    } 

    public void Unlock() 
    { 
     Monitor.Exit(myLock); 
    } 
} 

public class Tas_Lock : Lock 
{ 
    int L = 0; 

    public void Lock() 
    { 
     while (0 == Interlocked.Exchange(ref L, 1)) { }; 
    } 

    public void Unlock() 
    { 
     Interlocked.Exchange(ref L, 0); 
    } 
} 

Czy ktoś wie, co robię źle tutaj?

Edit: W odpowiedzi na Kevin:

Zmieniłem ją na następujące kwestie:

public class Tas_Lock : Lock 
{ 
    int L = 0; 

    public void Lock() 
    { 
     while (0 == Interlocked.CompareExchange(ref L, 1, 0)) { }; 
    } 

    public void Unlock() 
    { 
     Interlocked.Exchange(ref L, 0); 
    } 
} 

Jednak to wciąż powraca niespójnych wyników.

Edit # 2: Zmiany w C# blokady:

public class C_Sharp_Lock : Lock 
{ 
    readonly Object myLock = new object(); 
    bool lockTaken = false; 

    public void Lock() 
    { 
     Monitor.Enter(myLock, ref lockTaken); 
    } 

    public void Unlock() 
    { 
     if (lockTaken) 
      Monitor.Exit(myLock); 
    } 
} 
+0

Musisz użyć polecenia Porównaj w 'Zablokuj()', a nie w Exchange. Nie możesz zmienić wartości, jeśli blokada jest już zajęta. –

+0

Czy jest to zadanie na uczelni lub z kursu, który robisz? Jestem ciekawy :) – vtortola

+0

@vtortola Jest to część zadania na uczelni. – Heijmaaans

Odpowiedz

5

Ty nieporozumienie drogę Interlocked.CompareExchange prace. Zamiana wartości atomatycznie, jeśli wcześniej była równa podanemu porównaniu, i zwraca poprzednią wartość.

W skrócie, Interlocked.CompareExchange(ref L, 1, 0) będzie:

  • Sprawdź, czy L jest równe 0
  • Jeżeli L jest równe 0, to zestaw L 1 i powrócić do poprzedniej wartości (0)
  • Jeśli L nie jest równe 0 (a więc jest równa 1), a następnie powróci do poprzedniej wartości (1)

Stamtąd, co należy zrobić, to pętla Interlocked.CompareExchange zwraca 0 (co oznacza, że ​​została uzyskana blokada). W kodzie, czekasz whileInterlocked.CompareExchange zwraca 0.

Kod Poprawiono:

public class Tas_Lock 
{ 
    int L = 0; 

    public void Lock() 
    { 
     while (0 != Interlocked.CompareExchange(ref L, 1, 0)) { } 
    } 

    public void Unlock() 
    { 
     Interlocked.Exchange(ref L, 0); 
    } 
} 

Dwie rzeczy do uwaga:

  • Interlocked.Exchange w Unlock mogłyby zostać zastąpione przez szybciej Volatile.Write (lub nawet, choć można argumentować, prosty zapis)
  • Jeśli nie było to zadanie, można użyć wbudowanego -w klasie SpinLock, która już robi wszystkie te rzeczy w zoptymalizowany sposób
+0

Dzięki za wyjaśnienie! To naprawiło to. Zdaję sobie teraz sprawę, jak powinienem go użyć. – Heijmaaans

+0

Mam kolejne pytanie.Moja implementacja standardowego C# lock czasami zwraca wyjątek, ponieważ monitor.Exit próbuje wyjść, gdy blokada nie została podjęta. Aby rozwiązać ten problem, dodałem wartość bool, aby sprawdzić, czy blokada została podjęta przy użyciu przeciążenia metody Monitor.Enter, która ustawia wartość logiczną na true, gdy zostanie podjęta. Jednak teraz otrzymuję wyjątek argument mówiący, że parametr musi być zainicjalizowany jako false. Zmodyfikowałem mój wpis, aby zobaczyć moje zmiany. – Heijmaaans

+0

@Heijmaaans Upewnij się, że ustawiłeś lockTaken na false pod koniec Unlock –

Powiązane problemy