Proces nebo vlákno, modul nebo třída   zodpovězená otázka

VB.NET, Architektura, WinForms

Úvodem se přiznám, že v některých základních pojmech objektového programování mám prozatím trošku zmatek. Pokud proto přesahuje můj dotaz možnosti běžné rady v diskusi, buďte, prosím, k mým šedinám shovívaví a pošlete mne "do Paďous" s dostatečnou dávkou grácie, abychom si neničili tuto doposud slušnou diskusi :-).

Mám aplikaci, která docela dost ždíme (alespoň chvilkově) PC, proto některé zvláště náročné pasáže (animace obrázků) proháním samostatným vláknem, aby mi zbytek aplikace nezatuhnul a mohl plnit ostatní na něj kladené úkoly. Potud vše OK a funkční.

Nyní bych chtěl ale tuto aplikaci doplnit o další modul. A zde je jádro mého dotazu.

Ten nový modul bude více méně samostatný, se zbytkem aplikace nebude téměř vůbec spolupracovat - vytvoří si svoje okno (form) a bude si do něj kreslit či vypisovat svá data, která si natáhne ze svého datového souboru.

Vůbec nic by, teoreticky, nebránilo, aby tento modul byl v systému jako zcela samostatný program, spouštěný zcela samostatně.

Chci to mít v jednom projektu pouze ze třech důvodů- jednak to věcně patří k sobě, dále bych nerad zvlášť obsluhoval spouštění dalšího programu v systému a konečně, rád bych si do tohoto modulu natáhnul některá základní nastavení, která mám u aplikace uložena v konfiguračním souboru (my.settings....).

Protože se ale jedná o programově zcela samostatnou věc, rád bych to měl (hlavně pro přehlednost) strčené v něčem samostatném (modul, třída,....???) a hlavně, chtěl bych, aby si to běhalo ve vlastním vlákně (nebo jako samostatný proces?), abych nemusel řešit vzájemné časové konflikty (princip práce tohoto modulu bude také v animaci, takže nějaká časová smyčka,...).

Spustím to celé pouze jednou (nějakým příkazem při najetí hlavní aplikace).

Co byste doporučovali použít třídu, nebo jenom modul s vlastními programovými bloky a jak to pak celé spustit, aby si to mohlo dál žít svým životem ve svém vlastním vláknu (procesu)? (umím spustit třídní metodu do nového vlákna, ovšem po jejím ukončení vlákno zaniká, přitom já tady budu mít více vzájemně provázaných subrutin či funkcí - budu např. uvnitř modulu obsluhovat události apod.).

Nevím, je-li z popisu jasné, o co mi vůbec jde, ale snad mi někdo poradí, kudy nejlépe vykročit.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Opravdu jste nakousnul poměrně obsáhlé téma. Nevím jestli Vás úplně přesně chápu, ale zkusím popsat alespoň základ, další diskuze se může odvíjet z vašich otázek.

Formulář je třída. A proto, pokud chcete, aby běžel ve vlastním vlákně, tak si vytvořte v paměti novou instanci a tu zobrazte jiným vláknem. Je to to co potřebujete?

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

Předem bych Vám chtěl poděkovat za ochotu.

A k mému problému-myslím, že vyvěrá hlavně z toho, že některé drobnosti a střípky z programování ve VB již začínám tušit, leč unikají mi někde souvislosti.

Ale konkrétně.

Ano, tímto směrem jsem se začal ubírat, jenomže co mi uniká, jak a kdy se vloupat do toho nového vlákna. Asi nejzřetelněji to bude na příkladu (není to, samozřejmě, má "superaplikace" :-)), ale principiálně o co mi jde):

Dejme tomu, že mám 2 formuláře, Form1 (hlavní) a Form2 (ten chci, aby mi "běhal na svém písečku". Na Form1 mám tlačítko (ale jen pro názornost, jinak bych to chtěl strčit třeba hned do Form1_Load či pod.). Code vypadá takhle:

Public Class Form1
    Dim f As Form2



    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        f = New Form2()

        f.Show()

    End Sub
End Class
' = = = = = = = = = = = = = = = = = = = = = = =
Public Class Form2
    Dim WithEvents casovac As New System.Timers.Timer

    Private Sub casovacTick(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles casovac.Elapsed
        If Me.BackColor = Color.Blue Then
            Me.BackColor = Color.Red
        Else
            Me.BackColor = Color.Blue
        End If
       
    End Sub



    Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        casovac.Interval = 2000
        casovac.AutoReset = True
        casovac.Start()

    End Sub
End Class

Takto to funguje OK - form2 si dělá svou (v tomto případě velice smysluplnou) činnost, jenomže - ve stejném vláknu, jako Form1.

Pokud ale využiju Vaší rady a zkusím to zobrazit do nového threadu, jsem v úzkých, protože nevím, kdy (a jak) to do to thteadu poslat. Pokud Vás vezmu doslova, tedy "zobrazit", což asi odpovídá metodě "show", a upravím kód toho formu1 následovně:

Public Class Form1
    Dim f As Form2
    Dim thr As System.Threading.Thread

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        f = New Form2()
   
        thr = New System.Threading.Thread(AddressOf f.Show)
        thr.Start()

    End Sub
End Class

Tak mi ve chvíli zmáčknutí tlačítka Form2 pouze problikne a okamžitě zmizí.

Navíc, když jsem s tím laboroval včera a podobným způsobem se mi to podařilo spustit (přes nějakou jinou metodu ve Form2, už ale nevím jakou), tak jsem se dostal do dalšího problému. Protože instance f od Form2 je vytvořena v jednom (hlavním) vlákně a já se snažil ji obsluhovat z mého (vedlejšího) vlákna, debuger visualu mi vynadal!

Tak teď opravdu nevím, jestli je můj problém již jasnější, nebo jsem ho ještě více zamotal.

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

Tak to je poměrně jednoduché - místo f.Show použijte f.ShowDialog, díky čemuž se další příkazy v té proceduře provedou až po uzavření formuláře. Tím zajistíte, že vlákno zmizí po zavření formuláře a nemusíte se už o nic starat.

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

Čekal jsem, že se toto téma dostane k více projektům v jedné solution. Asi je to jiný problém. Zkusil jsem dva projekty, do každého na formuláři tlačítko, jeden z projektů vybral jako spouštěcí. V debug režimu se spustí, kompilace udělá jen jeden exe, ale ten už mi spustit nejde. Tak mi účel více projektů uniká, navíc jsem nepřišel na to, jak z jednoho komunikovat s tím druhým. Víte někdo víc?

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

Asi jste nejblíž - dalo by se to asi definovat jako více projektů v jedné solution jenom s tím, ža nejsem až tak náročný a nevyžaduji mezi nimi nějakou náročnou komunikaci.

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

Pane Hercegu, děkuji i Vám za snahu - vím, že se mnou máte fůru problémů, ale mně jde dnes o něco přesně opačného, než jste mi pomohl vyřešit minule. Minule jsme spolu řešili starost, aby se mi nová vlákna nehromadila a aby se včas ukončila.

Dnešní problém je přesně opačný.

Potřeboval bych, aby aplikace ve Form1 jela i nadále a vykonávala činnosti, na které byla navržena (mimo jiné v určitých intervalech pouští do svého okna animace obrázků, které jste mi pomohl minule optimalizovat).

No a k tomu, abych nějak spustil "druhou aplikaci" - v tomto příkladě zastoupenou tím form2, která tvoří svou vlastní činnost ve svém vlastním okně - s form1 by měla mít pouze toliko společného, že bych aplikaci spřaženou s form2 rád spustil z aplikace ve form1 (ale dál se o ni již aplikace form1 starat nemusí, nanejvýš by bylo vhodné, aby se aplikace z form2 ukončila v případě ukončení aplikace form1), a ještě bych rád využil některých proměnných společného nastavení z konfiguračního souboru celého sestavení (z my.settings.proměnná).

Toť vše, aplikace si nebudou vzájemně kreslit křížem do "cizích" prostorů, nebudou se ani nijak vzájemně ovlivňovat...

Já ví, že to asi blbě vysvětluju, tak ještě snad jak by měl vypadat výsledek:

Mám hlavní aplikaci, která bude na základě nějakého vysílacího plánu odbavovat určité pořady do televizního vysílání (pořady jsou zkombinovány z videa, PPT prezentací a fotoslideshow (+ samozřejmě nějaká hudba).

Toto všechno mám nasměrováno na "Form1", který není ve fulscreen módu (aby nebyl stále nahoře), ale zabírá celou plochu obrazovky. Protože jedu na víceobrazovkové konfiguraci, mám v my.settings.screen uloženo číslo obrazovky, na který chci promítat)

Potud vše postupně skládám a, alespoň po částech, to jakž takž funguje.

Jenomže jsem si vymyslel ještě další zhovadilost. Chci vyrobit další část aplikace, která si (bez ohledu na jakékoliv vysílací schéma a na to, co právě "běží na hlavním Formu") bude kontrolovat obsah nějakého souboru na disku a v případě, že v něm bude nějaké "důležité sdělení", bude toto v určitých intervalech vysílat jako běžící baner přes vysílání ve Form1. Jinak by měla udělat to, že si (v my.settings) zjistí obrazovku kam vysílat, vytvoří form s barevným pozadím přes celou šířku této obrazovky ("barevný pruh"), který je definován jako Allways on top a na něj odvysílá své sdělení. Když text doběhne, form se (nejlépe přes Fade) schová, aplikace si zapne časovač na nastavenou dobu a po této době opět vysílání opakuje.

Jak jsem psal ve svém prvním dotazu, je to skoro dělané tak, abych to vytvořil jako zcela samostatnou aplikaci (samostatné exe) a spouštěl to přímo ze systému, ale rád bych věděl, je-li to možné udělat v rámci jednoho sestavení (důvody také popisuju v prvním příspěvku).

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

Aha, teď to už asi chápu. Takže do procedury, kterou spouštíte v jiném vlákně, dejte tuto smyčku:

While Not Konec
    'kontrola souboru (doporučuji nekontrolovat v každém průchodu, ale jen např. jednou za 10 sekund, ale to je na vás) a případně přehrát celé zprávy formuláře
    '...

    'čekat určitou dobu
    Threading.Thread.Sleep(50)
End While

Do projektu si přidejte modul (Add Item\Module) a do něj dejte tuto deklaraci:

Public Konec As Boolean = False

Při ukončování aplikace vždy nastavte hodnotu této proměnné na True, aby se ukončila smyčka v našem vlákně.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Děkuji, (sice jinak, než jsem si přál), ale odpovídáte mi na můj dotaz.

Jestli to tedy správně interpretuji to, co jsem zamýšlel, abych "NĚCO" spustil tak, že si to bude žít svým životem, nelze. (v tom "NĚCO" jsem totiž měl na mysli využívat události - třeba změnu souboru řešit jako událost z FileSystemWatcher, dále událost doběhnutí časovače, atd. - tedy ne toliko jednu proceduru).

Asi se tedy programově dá spustit do jiného vlákna pouze jedna konkrétní osamocená procedura (samozřejmě asi s možností využívat volání jiných funkcí a procedur) (což zvládám a v jiných částech mé aplikace to docela funguje), ale nejde říct programu (pokud se vrátím k mému příkladu s Form1 a Form2):

1) tak a teď spusť instanci Form2 v novém vlákně, a nech ji "žít" i po vytvoření a čekat na své události.

2) Až nastane událost timeru definovaném v této form2 obsluž ji procedurou definovanou v této form2 a v témže vlákně, jako jsi spustil celou instanci Form2.

Takže to budu muset obejít nějak jinak. Dík za radu, že touto cestou nelze - i takové rady jsou důležité.

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

Já jsem neřekl, že to nejde. Ten formulář běží v jiném vlákně, akorát má svou smyčku. Ono by se to dalo řešit i jinak (smyčka rozhodně není čisté řešení), ale nebylo by to vůbec jednoduché. Pokud to dáte do jednoho formuláře, ušetříte si spoustu práce.

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

Omlouvám se, špatně jsem se ve spěchu vyjádřil (manželka již někde stepovala na rohu ulice a musel jsem pro ni), měl jsem spíše napsat, že to nejde v intencích mých znalostí. Život mne naučil, že slovní spojení "to nejde" je v 99% případů alibistické vyjádření skutečnosti, že buď, "to neumím", nebo ,ještě hůře, "se mi nechce. Ale i tak dík.

P.S: přece jenom mi to nedá. Asi to sice v mém případě nepoužiju, ale trochu jsem se Vaší radou zabýval.

Takže, pokud chci tímto způsobem spustit ten druhý formulář (stále pro jednoduchost vycházím z toho svého příkladu s Form1 a Form2) aby běhal v jiném threadu, je nutné ho "nějak zaměstnat", aby stále něco dělal - jinak se thread ukončí?. Není tedy možné, aby tento formulář pouze "čekal na událost" tak, jak se jeví stav normálního formuláře, který spustím přímo, případně metodou f.start() ve stejném vlákně?

Předpokládám (ale chtěl bych to ověřit), že u takovéhoto formuláře ošetření zde vzniklých událostí běží ve stejném vlákně, v jakém je formulář spuštěn (tedy ne v tom původním, ze kterého byl spuštěn).

Pokud bych chtěl Vaší konstrukce (se sleepem) využít, ale chtěl bych, aby mi formulář Form2 pracoval stejně, jako všechny jiné formuláře (reagoval na události atd.), kam tu "udržovací" smyčku nejlépe umístit?:

zkoušel jsem 2 možné způsoby, které se navenek jevily podobně:

1) druhý formulář jsem spouštěl stejně jako v příkladu výše, tedy

thr = New System.Threading.Thread(AddressOf f.show)
        thr.Start()

a do Form2 jsem dal "udržovací" smyčku do události me.activated, tedy:

Private Sub Form2_Activated(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Activated
    While Not konec
       Threading.Thread.Sleep(100)
       Application.DoEvents()
    End While
End Sub

Druhý způsob, který mi (na základě vnějších znaků) fungoval stejně byl, že jsem do Form2 přidal další veřejnou metodu:

Public Sub pracuj()
   Me.Show()

   While Not konec
       Threading.Thread.Sleep(100)
       Application.DoEvents()
   End While
End Sub

a celé to spouštěl z Form1:

thr = New System.Threading.Thread(AddressOf f.pracuj)
        thr.Start()

.

V obou případech se Form2 objevil, byl přístupný uživateli a reagoval na vlastní události (měnil barvu v rytmu časovače).

Mé dotazy:

1) Je takový přístup možný, pokud ano, který z nich je lepší (u toho prvního si nejsem zcela jist, že jsem zvolil nejlepší metodu ošetření události, kam to dát - potřeboval bych tu skutečně poslední, která se vyvolá až po sestavení a vykreslení formuláře a, přiznám se, to nevím, která je)

2) A potvrzení - vyvolané události jsou asi taky ošetřovány v mém "vedlejším" vláknu "thr" - asi jo, protože nejsou problémy s přístupem k vlastnostem samotného formuláře, ale přece jenom si to nechám od odborníků potvrdit.

Jinak ale by se tato Vaše konstrukce dost blížila mým původním požadavkům na chování aplikace, jediné rozpaky mám nad tím sleepem - jak se odrazí na celkové výkonnosti - ale ostatní vlákna by snad nijak (výrazně) ovlivňovat neměl a při vykonávání výkonově náročných operací v rámci mého Form2 by asi nebyl velký problém na tuto dobu ten udržovací cyklus přerušit a nahradit cyklem, který bude provádět právě ty "náročné" operace. Nebo se (opět) mýlím?

Ještě jednou dík.

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

Ono by to ve skutečnosti mělo být přímo v té metodě, kterou spouštíte v druhém vlákně. Ta metoda si formulář otevře zavoláním Show a čeká, dokud není konec programu. Ale zkuste to spíš dát do stejného formuláře, ušetříte si hodně práce a ta smyčka není moc dobrá.

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

Děkuji, Vašich rad plně využiji a když jsem to dal "na papír", tak to zas až tak kostrbatě nevypadá a hlavně to plní mé představy ohledně funkcionality. A díky Vám jsem zase o kapánek chytřejší (neměl byste pro mne po ruce místo kapátka nějakou větší konev? :-))

A pokud ještě mohu, měl bych ještě jeden, spíše teoretický, dotaz - nemusíte mi, samozřejmě, odpovídat, pro mé praktické potřeby Vámi sdělené prozatím plně postačuje - jen by mne zajímalo, co je za tím vším (dá-li se to ale vůbec laikovi jednou větou vysvětlit):

Patřím ke generaci, pro kterou událostmi řízené programování mohlo být pouze více či méně nedostižným snem. V tehdejších programech (alespoň po tu dobu, kdy jsem se jimi jako kluk nějak bavíval) jakékoliv ošetření jakékoliv události (hlavně vstupy od uživatele, ale i jiné) představovaly buď "zastavení" programu dokud to uživatel nepotvrdí, nebo různě důmyslné smyčky, ve kterých se čekalo a testovalo, zdali se někde něco nestalo.

No a teď mi dáte Form, na který dle Vašich rad plácnu Label a Buton, spustím a form "nic nedělá", jen zeje na obrazovce a čeká, až se mi uráčí zmáčknout tlačítko, aby do Labelu vítězoslavně napsal "Hello world!" a zase nic, zase čeká (ač už nemá na co).

Z principu funce počítače je jasné, že program jako takový nezná stav "čekání", že stále něco musí dělat, proto předpokládám, že ty "čekací smyčky", nebo jejich obdobu, při kterých se kontrolují jednotlivé události, jestli náhodou nějaká nenastala, za nás připravili tvůrci běhového prostředí, a že někde hluboko v mašině tedy něco takového (bez našeho vědomí a možnosti to ovlivnit) stejně probíhá.

Doposud jsem tuto skutečnost bral za (milou) samozřejmost, protože se člověk nemusí starat o fůru neproduktivních věcí. Ale při řešení problému z tohoto vlákna diskuse jsem chtě nechtě došel k otázce:

Kdo, kde nebo jak je řízen tento "vnitřní život" aplikace? Jaktože, když spustím Form1, tak se bude takto chovat (skončí, až mu to sám nařídím), dokonce když spustím konstrukcí

dim form2 as new form
form2.show()

z tohoto Form1 další formulář, bude se chovat úplně stejně (taky musím jeho destrukci nějak iniciovat),

ale pokud toto spuštění Form2 nasměruji do jiného vlákna, pak je již zcela bez "vnitřního života" a na rozdíl od předchozích možností zanikne okamžite, jakmile dokončí metodu, kterou byl zavolán.

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

Vím, že to asi nebude to co chcete slyšet, ale proč to neuděláte všechno na jeden formulář s jedním Timerem, který bude obsluhovat případné konktroly nových zpráv?

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

Ano, tudy asi cesta povede.

Jediný důvod byl v přehlednosti (dělá si to svou práci, takže logicky by to líp vypadalo oddělené), případně by se to mohlo jako fragment nechat použít i v jiných projektech. Proto jsem se snažil zjistit možnosti VB, jestli něco takového umí.

Asi to tedy budu řešit jako ostatní moduly své aplikace (které se liší hlavně tím, že kreslí na plichu toho mého Formu1), že si to udělám jako další třídu a do samostatného vlákna budu spouštět pouze ty časově náročné operace ("stmívání a roztmívání" samotného Formu a pak, samozřejmě, animaci toho textu).

Ještě jednou dík všem!

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