Vítejte na blog.vyvojar.cz Přihlásit | Registrovat | Pomoc

Ars programatica

Udělat dobrý sotware je řemeslo, udělat výjimečný software je umění
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.

Posted: Thursday, July 03, 2008 5:31 PM by pbouda
Vedeno pod: , ,

Komentář

pazu napsal:

Upozornění na přehlížený problém dobré, ale držel bych se zavedené terminologie:

vyjímky se vyhazují, nezdvihají

události se taky nezdvihají, ale vyvolávají

k dispose - propustit je celkem hezké slovo, ale jelikož idiom dispose of garbage se česky řekne zbavit se odpadu, držel bych se nějaké té likvidace

# July 4, 2008 12:14 AM

pbouda napsal:

pazu: Obávám se, že žádná ustálená česká terminologie neexistuje. Co se týče "disposed", o likvidaci bych v této souvislosti nemluvil, to je záležitost GC. Ještě mne napadlo "uvolnění", to by nakonec možná bylo i lepší.

Zdvihnout, nebo vyhodit výjiku? Raise or Throw? "Vyhodit" se mi zdálo příliš hovorové, tak jsme zvolil "zdvihnout", ale uznávám, že klíčovému slovu throw by lépe odpovídalo vyhodit.

S událostmi souhlas, ty se vyvolávají, na tom shodneme.

# July 4, 2008 9:29 AM

Vitezslav Gazda napsal:

Zadne prekvapeni, stejny problem by se dal predvest na obycejnem nemodalnim formulari:

using (Form form = new Form())

{

 form.Show();

}

Uplne stejny problem kdy je formular uvolnen a pritom je jeste zobrazen.

V pripade modalniho formulare uz je to vsak zcela vporadku.

using (Form form = new Form())

{

 form.ShowDialog();

}

# July 4, 2008 3:21 PM

pazu napsal:

Ano, terminologie kolísá. Uvolnění mi zní asi nejlíp, je pravda, že likvidace vyvolává dojem odstranění z paměti. Vyhodit vyjímku aspoň v mých kruzích je celkem zažité -> už z dob C++. Asi je to odvozeno od "vyhodit chybu", což se myslím dost používá a většinou vyjímka se vyhazuje (háže?) když je nějaká chyba.

# July 4, 2008 3:56 PM
Nejsou povoleny nové komentáře k tomuto příspěvku