Vice vlaken, ano ci ne?   zodpovězená otázka

VB.NET, Threading

Nemam zkusenosti s vice-vlakny, ale zaujal me priklad nasazeni vice-vlaken v situacich, kdy nejaky objekt dela narocnou operaci (zpracovava vice souboru, apod.) a my potrebujeme aktualizovat progress bar na formu.

Tohle jsem vzdy zatim resil pomoci udalosti. Tj. objekt ktery delal intenzivni praci "obcas" vyvolal event (po kazdem souboru, atd.), ktery jsem zpracoval rutinou ve formulari: zaktualizoval progress bar, spustil doevents, pripadne prikazal objektu, aby ukoncil cinnost kterou delal - abort, atd.

Jak obvykle resite aktualizaci progress baru? Pouzivate kvuli tomu vice-vlaken?

BTW: Chapu, ze pokud bych chtel mit napr. nejakou animaci vyzadujici "konstantnich" X snimku za sec, asi musim sahnout po vice vlaknech, ale jen kvuli progress baru?

P.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Použití více vláken je na místě u jakékoliv akce, která může trvat dlouho. Nechat kvlli tomu zasekané hlavní okno aplikace je prasárna a DoEvents je jenom berlička, jak tu prasárnu navenek schovat.

Cokoliv, co trvá déle, je ideální dělat v separátním vlákně. Je s tím sice trochu sena, ale není to tak strašné a aplikace nebude ve stavu neodpovídá, ale plně funkční. Samozřejmě při práci s vlákny je potřeba dávat pozor na synchronizaci přístupu ke sdíleným datům a při aktualizaci komponent formuláře z jiného vlákna použít SynchronizationContext nebo Invoke.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Proc je DoEvents jenom berlicka? Hlavni okno se tvari jak ma, dokonce s nim lze hybat, apod. Chapu, ze timhle zpusobem zbytecne brzdim ten narocny proces, ktery by mohl jet na plne obratky, ale to je vykonostni rozmer, o kterem vim.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Proč je použití DoEvents hovadina se zde již několikrát řešilo a dokonce jsem to zde poměrně low-level rozebíral. Hledejte DoEvents.

Aktualizaci ProgressBaru je nejlepší řešit asynchronním vyvoláním události přes WindowsFormsSynchronizationContext.Post.

Na filozofickou otázku "Vice vlaken, ano ci ne?" vám odpovím jak kdy, ale většinou ano. Například pokud zpracováváte soubor, nevyplatí se ho zpracovávat několika vlákny současně, protože zde je brzda ono I/O zařízení a bylo by to pomalejší než zpracování jedním vláknem.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Moc děkuji za tip ohledně WindowsFormsSynchronizationContext.Post. O tom jsem neměl ani tušení - moc jsem to v programech zatím neviděl. Určitě se na to soutředím.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Předem ale upozorňuji, že pokud to i nadále hodláte dělat v hlavním vlákně aplikace s pomocí DoEvents, potom WindowsFormsSynchronizationContext zcela postrádá smysl a jen by to ještě zpomalil. WFSC je určen pro volání Windows Forms komponent z jiného vlákna než byly vytvořeny, je to náhrada a doporučený postup místo BeginInvoke/Invoke optimalizovaný pro Windows Forms.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Tak to asi opravdu ztrácí význam protože se snažím oddělit prezentaci od provádění aplikace, tj. raději předám informace o stavu (v jakém se nacházím stavu, kolik mi toho zbývá, apod. a "neřeším" jak to formulář zobrazí) než abych přímo přistupoval k formulářovým prvkům z toho objektu, který dělá časově náročnou činnost. K prvkům formuláře přistupuji vždy a pouze jenom z daného formuláře - snažím se, aby pro mně bylo tabu přistupovat k prvkům formuláře z jiných objektů.

Teď to řeším tak, že zpracovávám X (tisíce) souborů a po každém souboru vyvolám event (0.3sec-0,7sec), kterému předam objekt s informacemi o stavu. Formulář si tenhle event zpracuje a zavolá DoEvents.

Je pravda, že pokud by formulář danou událost nezpracoval, aplikace by se "zasekla" na několik minut než by se zpracovaly všechny soubory.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Pokud jde o takovouto akci, rozhodně použijte vlákna. Nový event vám může do formuláře hodit vlákno a vy z něj akorát zavoláte obsluhu ProgressBaru (přes ten SynchronizationContext nebo Invoke).

Anebo můžete použít komponentu BackgroundWorker.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Vy nechápete základní principy vláken ve Windows Forms. Pokud vytvoříte nové vlákno v němž budete provádět náročnou proceduru, která bude vyvolávat událost s informací o průběhu a na tuto událost bude napojena metoda z nějakého formuláře (ve kterém se bude aktualizovat ProgressBar), bude tato metoda provedena v onom novém vlákně a tudíž budete přistupovat k prvkům formuláře z jiného vlákna než ve kterém byly vytvořeny. Zde právě přichází ke slovu SynchronizationContext nebo Invoke. Metoda která je prováděna v novém vlákně samozřejmě o nějakém formuláři nemá zdání a je od něj izolována, pouze provede to co je napojeno na jí vyvolávanou událost.

P.S.: Oddělení prezentační a business logiky je uplně něco jiného než co se snažíte udělat vy.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Uvedený postup byl uveden jako popis současné verze bez vláken. Je pochopitelné, že je nutné uvedený přístup změnit, aby to bylo vhodné pro nové vlákno. Chápu, že uvedený postup nelze bez modifikace s novým vláknem použít. Děkuji za vysvětlení.

Našel jsem zajímavý článek, který k tomu účelu využívá BackgroundWorker:

http://www.codeproject.com/KB/progress/P....

To se mi zdá jako pohotové řešení.

P.S. Ad různé vrstvy aplikace. To vím, záměrně jsem to nenazval vrstvami. Jen jsem chtěl říct, že zásadně nepřistupuji k formulářovým prvkům z jiných objektů. U hodně aplikací to vidím používat a to se mi zdá zvěrské.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Zvěrské to není, jen se to nesmí nevhodně používat. Například VB.NET má narozdíl od C# ve výchozím stavu všechny ovládací prvky jako Friend, tj. lze k nim přistupovat z libovolné třídy v rámci Assembly. BackgroundWorker je celkem dobrá věc, ale hodí se pouze na jednoduché věci.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Friend jsem si všiml, to mi přijde zvláštní. Takový přístup totiž dle mého názoru brání znovupoužitelnosti tříd. Ale asi to je pouze o zvyku.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Můžete mi vysvětlit, čím by Friend bránil znovupoužitelnosti třídy??

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Friend sám o sobě ne, ale přistupování k prvkům formuláře z jiných tříd ano.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Nemáte pravdu, ničemu to nevadí. Zaprvé máte stejně přístup ke všem ovládacím prvkům formuláře přes veřejnou vlastnost Controls a za druhé kdybyste dal všechny ovládací prvky jako Private a chtěl s nimi pracovat zvenčí, tak stejně musíte napsat nějaké vlastnosti/metody které by tyto prvky obsluhovaly a to je zcela zbytečná práce. Dále Friend znamená viditelnost pouze v rámci Assembly, nikde jinde ne (z jiné Assembly) což je žádoucí.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Nemluvím zde o technické proveditelnosti - ta je zřejmá a funkční. Ale mluvím o žádoucím/nežádoucím přístupu.

Když si posléze někdy budete chtít zkopírovat objekt poskytující nějakou činnost, tak ten se bude odkazovat na formulář/ovládací prvek, který v nové aplikaci ani nemusí být, jeho instance se může jmenovat úplně jinak, apod.

Nevěřím, že používat v třídách kód ve stylu

OpenForms(0).TextBox1.Text = "cokoli"
OpenForms(1).Label1.Text = "nic"

je zcela neškodný programátorský přistup. Možná bych ještě dokázal pochopit, kdybych třídě předal odkaz na formulář v konstruktoru a ta by pracovala s tímhle odkazem, ale odkazovat se přímo na instance formuláře/ovládacích prvků uvnitř tříd i v rámci jedné assembly se mi nezdá jako OK.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Právě z tohoto důvodu jsou ovládací prvky označeny jako Friend a ne jako Public. To znamená, že v rámci jedné Assembly (jedné aplikace) s tím můžete pracovat výše uvedeným postupem, tj. odkazovat se přímo na ovládací prvky (věřte tomu že se to hodí a že se to používá). Tím že je to jedna aplikace tak vždy víte co děláte. Kdybyste chtěl tuto třídu použít v jiné Assembly, tak tyto Friend členy nebudou přístupné. To co říkáte vy je pravda v případě knihoven nebo komponent, u kterých je předem známo, že je budou používat jiné aplikace (zapouzdřenost je jednou z hlavních vlastností OOP), ale ne v případě formulářů v rámci jedné aplikace. Pokud máte strach že by vám někdo Friend ovládací prvek mohl omylem nastavit na Nothing (null) tak to samé lze udělat pomocí Public kolekce Controls.

Mimochodem vámi výše uvedený kód by nefungoval (pokud byste nepoužil Late-binding což doufám nepoužívá nikdo), protože kolekce OpenForms je typu Form (musel byste na základě něčeho přetypovat na konkrétní formulář který tyto ovládací prvky obsahuje).

nahlásit spamnahlásit spam 0 odpovědětodpovědět

:) S tím kódem máte samo pravdu.

To jsem trochu překvapen, že se formuláře používájí také tímhle způsobem, ale určitě nechci polemizovat. Domníval jsem se, že to je nepsané tabu.

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.
  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback