2013-09-30 14 views
6

Co się stanie, jeśli zadzwonię pod numer Dispose() na zablokowanym obiekcie?Czy mogę usunąć zablokowany obiekt?

lock (obj) 
{ 
    obj.Dispose(); 
} 

A co się stanie, jeśli pominąć Monitor.Exit() połączenie w tym przypadku:

Monitor.Enter(obj); 
obj.Dispose(); 
+3

Dlaczego chcesz to zrobić? – tomsv

+0

Nic się nie stanie, będzie działać gładko. Ale dlaczego? –

+1

+1 Interesujące pytanie, choć można sobie wyobrazić, byłoby to coś, co każdy robi w praktyce. –

Odpowiedz

5

Co się stanie, jeśli wezwę funkcję Dispose() na zablokowanym obiekcie?

Po pierwsze, sam obiekt nie jest zablokowany (zabezpieczony) na samym początku. Odniesienie wykorzystywane w kluczowych lock służy do zaznaczenia lub oznaczyć fragment kodu, który nie należy uruchamiać jednocześnie z innym (lub sam) sekcji kodu używanego odniesienia do obiektu samo. W rzeczywistości nie wpływa to na sam obiekt. Jest to dość powszechne błędne przekonanie o tym, jak blokady działają w .NET.

W tym kontekście wywołującego Dispose nie jest inaczej niż wywołanie innej metody. Nie ma nic szczególnego. Oznacza to po prostu, że dwa różne wątki nie mogą jednocześnie wykonywać Dispose. To, co robisz, jest nie tylko akceptowalne, ale jest zalecane, jeśli klasa nie jest bezpieczna dla wątków.

A co się stanie, jeśli pominąć Monitor.Exit() wywołanie w tym przypadku:

Należy zawsze uwalniają śluz. Pamiętając o tym, że zamki nie działają na podstawie samego odniesienia do obiektu, powinno być łatwiej zrozumieć, że pozostawiałbyś zamek w stanie nabytym. Nie ma znaczenia, że ​​obiekt jest usuwany. Pamiętaj, że Monitor (lub lock) nie blokuje ani nie chroni samego obiektu. Zaznacza lub oznacza tylko część kodu. Jeśli wątek spróbuje uzyskać blokadę z tym samym obiektem ponownie, ten wątek będzie zmuszony czekać w nieskończoność, co może spowodować zakleszczenie.

Bardziej interesujące pytanie brzmi, czy może to doprowadzić do wycieku pamięci. Czy rootuje obiekt na Monitor.Enter? Odpowiedź brzmi nie. Można to wykazać na poniższym przykładzie.

public class Program 
{ 
    public static void Main(string[] args) 
    { 
     var foo = new Foo(); 
     Monitor.Enter(foo); 
     foo = null; 
     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 
     Console.ReadLine(); 
    } 
} 

internal class Foo 
{ 
    ~Foo() 
    { 
     Console.WriteLine("~Foo"); 
    } 
} 

Jeśli skompilujesz i uruchomisz to zauważysz, że drukowane jest "~ Foo". Sugeruje to, że Monitor.Enter nie posiada wewnętrznie odniesienia. Więc nie jest wskazane, aby pominąć wywołanie Monitor.Exit, ale możesz mieć pewność, że nie spowoduje to przecieku pamięci.


Właściwie, to nie może być do końca prawda. Chociaż dość łatwo jest wykazać, że nie ma zarządzanego wycieku pamięci, rzeczy mogą się różnić w niezarządzanej rzeczywistości. Nie patrząc na kod SSCLI, naprawdę nie wiemy, co robi wewnętrznie Monitor.Enter. Być może to alokuje jeden dodatkowy slot tablicy (lub cokolwiek innego) w niezarządzanej sterty lub stosie. Chciałbym myśleć, że Microsoft rozważał ten niejasny scenariusz, ale kto wie. Chodzi o to, że połączenia Monitor.Enter i Monitor.Exit powinny być zawsze sparowane, abyś nie musiał się tym martwić.

+0

Dzięki! Wszystkie odpowiedzi są dobre, ale wybrałem twoje jako najbardziej szczegółowe. –

4

The Dispose paradygmat jest odmienna od tej, lock. Możesz bezpiecznie pozbyć się obiektu, na którym aktualnie masz blokadę wzajemnego wykluczenia; jednak nadal powinieneś zwolnić blokadę. Jeśli tego nie zrobisz, inne wątki, które wywołają Monitor.Enter na (teraz ułożonym) obiekcie będą blokować w nieskończoność.

1

Na pierwsze pytanie - co się dzieje, jest to, że jedynym zagrożeniem w każdej chwili może zadzwonić Dispose(). Dla twojego drugiego pytania - jeśli wiele wątków uruchomi ten kod, pierwszy z nich wywoła metodę Dispose. Reszta będzie blokować na zawsze.

Istnieje nic szczególnego w metodzie dispose pod numerem. Jest tak jak każda inna metoda, z tym wyjątkiem, że bierze udział w jakimś cukierku syntaktycznym (używając na przykład instrukcji).

6

Implementacje Zużyty ogół nie są thread-safe.

To dlatego, że nie należy go wyrzucać obiektu natomiast nadal istnieją odnośniki, które można zastosować. Oznacza to, że nie należy pozbywać się obiektu, do którego odniesie się jakikolwiek inny wątek.

IDisposable/Dispose jest sposób zarządzający obiektu życiu, albo w ogóle lub drugiej nici - to paradygmat do uwalniania zasobów obiektu po jej żywotność jest na. (Instrukcja using jest idiomem/paradygmatem do użycia, w którym właściwym czasem życia obiektu jest zakres instrukcji.)

Tak więc, jeśli wywołujesz utylizację, gdy inny wątek ma zablokowany obiekt, coś już poszło bardzo źle.

1

zawsze można zablokować za pomocą istniejącego obiektu, metoda Dispose nie usunie obiekt, więc odniesienia do obiektu wciąż istnieje. Metoda Dispose() nie wpływa na instancję obiektu.

Powiązane problemy