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ć.
Dlaczego chcesz to zrobić? – tomsv
Nic się nie stanie, będzie działać gładko. Ale dlaczego? –
+1 Interesujące pytanie, choć można sobie wyobrazić, byłoby to coś, co każdy robi w praktyce. –