Dispose Pattern a multi-threading
Dispose Pattern,
neboli použití klausule
using v kombinaci s třídou
implementující interface
IDisposable,
je v .NET světě známá a běžně používaná technika. Typické použití vypadá
následovně:
public class Foo :
IDisposable
{
// implementation
}
using (Foo foo = new
Foo())
{
Bar.DoSomething(foo);
}
Problém může nastat, pokud se s foo pracuje v jiném threadu, než ve kterém je kód
realizující dispose pattern, například pokud Bar.DoSomething(foo)
vytvoří pro práci s foo nový thread. V
tom případě totiž řízení přejde "neprodleně" na uzavírací závorku using
klauzule, je zavoláno foo.Dispose() a Bar.DoSomething()
může dále pracovat s objektem, který již byl propuštěn*), nebo mu je propuštěn "pod rukama".
Pokud je metoda Bar.DoSomething(foo)
schopna se s takovou situací nějak vypořádat, aniž by zdvihla výjimku, může to
být příčinou různých tajuplných záhad. Jako v případě, který mne k napsání toho
článku inspiroval:
Používáme XtraReport
ze sady komponent DevExpress;
reporty otevíráme v náhledu pomocí metody XtraReport.ShowPreview().
V domnění, že tím něčemu prospěji, napsal jsem během refactoringu zhruba
následující kód:
MyReport report =
new MyReport();
report.DataSource = some data source;
using (report)
{
report.ShowPreview();
}
Jaké bylo moje překvapení, když se otevřelo okno náhledu a v
něm místo reportu (který se předtím zobrazoval normálně) strohé oznámení:
"Tento dokument neobsahuje žádné stránky." V domnění, že na vině jsou
špatně vrácená (žádná) data z databáze, nebo že je špatně nastavený databinding,
hledal jsem chybu poměrně dlouho všude možně, jen ne tam, kde skutečně byla: příčinou
byla totiž právě výše zmiňovaná vlastnost, kdy XtraReport.ShowPreview()
někde uvnitř využívá jiný thread, takže se řízení "neprodleně"
vrátilo na uzavírací závorku using
klauzule a report byl propuštěn pomocí Dispose(),
což ovšem dialogu pro preview vůbec nevadilo a zobrazoval prázdnou plochu s
výše citovanou hláškou.
*) Váhal jsem, zda
nějak překládat "disposed". A nakonec jsem se rozhodl, že ano, a
zvolil jsem překlad propuštěný/propuštěnec podle vzoru propuštěný otrok.