Pagina 1 di 3 1 2 3 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 24
  1. #1
    Utente di HTML.it
    Registrato dal
    May 2005
    Messaggi
    40

    [c#] Eventi e Multi-thread

    Ciao a tutti,

    sto studiando la possibile struttura di un programma da realizzare e non voglio perdere l'occasione di chiarirmi un pò di punti: non mi piace far le cose senza capirle!

    Premessa: non specifico i parametri che vengono passati ai metodi ma ovviamente li ho messi. Si tratta di una console application, nessun elemento grafico.

    Scenario

    - Il thread principale del mio programma istanzia N oggetti della classe OBJ1. Ciascuno di questi oggetti contiene un evento definito al suo interno e un metodo DoWork() che poi richiama questo evento passandogli codici diversi a seconda della segnalazione che vuole fare.
    - Il thread principale istanzia anche un altro oggetto OBJ2 che dovrà gestire tutta la parte di tracciamento log ed errori, conunicati a lui dalle varie istanze di OBJ1 tramite il loro evento: per far questo associa ad ognuno degli N eventi (uno per ognuna delle N istanze di OBJ1) il metodo LogManagement() dell'istanza di OBJ2 come gestore dell'evento.
    - Il thread principale crea e lancia gli N metodi DoWork delle N istanze di OBJ1 in N thread secondari paralleli.
    Poi il thread principale sta in wait (questo aspetto non è importante ai fini della discussione) mentre le N elaborazioni parallele fanno il loro.

    -> A questo punto mi aspetto che gli N processi secondari possano in parallelo lanciare il loro evento ogni volta che è necessario e in automatico l'oggetto OBJ2 tenga traccia di quanto accade ad esempio scrivendo in un file TXT tutti i messaggi relativi agli eventi sollevati.

    Ho però diversi dubbi e vorrei analizzare a fondo la questione, che credo utile e interessante a livello generale. Elenco le mie considerazioni/incertezze aspettando poi dagli altri utenti conferme o smentite.

    1) Tutti i thread condividono la stessa area di memoria per cui possono tutti puntare i vari "(s)oggetti" di cui sopra, ovviamente prestando attenzione alle problematiche del multi-thread cioè stando attenti a non accedere mai contemporaneamente a risorse condivise.
    In teoria qualsiasi oggetto istanziato in un thread è accessibile dagli altri visto che l'area di memoria è condivisa, giusto?

    2) Quando un thread lancia un evento in pratica è come richiamare un delegate cioè un puntatore a funzione: il thread che lancia l'evento salterà dal flusso di istruzioni in cui si trova a quello della funzione che gestisce l'evento e una volta finito riprenderà l'esecuzione del flusso dalla riga successiva a quella che ha scatenato l'evento. Il meccanismo non è quindi asincrono bensì sincrono. Corretto? Inoltre la chiamata dell'evento, e quindi la chiamata della funzione che lo gestisce, comporterà il caricamento nello stack del thread in questione di una copia delle istruzioni da eseguire per cui nel caso in questione avremo potenzialmente N stack (relativi agli N thread) in cui contemporaneamente vi sarà caricata una copia della funzione LogManagement(). Siete d'accordo?

    3) A questo punto l'aspetto delicato sarà gestire eventuali accessi da parte di tutte queste repliche parallele del LogManagement() a membri di OBJ2 (che saranno condivisi in quanto l'oggetto è uno solo) o ad esempio ad un file TXT dove si va a scrivere. Siete d'accordo?

    Alla luce di queste considerazioni sarei portato a chiedermi che differenza ci sarebbe se invece che usare gli eventi io passassi ad ogni costruttore di OBJ1 (dal thread principale) il puntatore all'oggetto OBJ2 e potessi quindi poi richiamare da ogni thread secondario il metodo LogManagement() direttamente.

    Trovo questo scenario interessante come spunto di rilfessione per capire bene come funzionano gli eventi e come uno scenario multi-thread possa farne uso.

    Attendo commenti e correzioni alle mie affermazioni...

    Grazie

  2. #2
    Moderatore di Programmazione L'avatar di alka
    Registrato dal
    Oct 2001
    residenza
    Reggio Emilia
    Messaggi
    24,472

    Moderazione

    Ho spostato la discussione all'interno del forum dedicato a Visual Basic (VBx, VBA, VBScript, ...) e ai linguaggi per .NET Framework (VB.NET, C#, ...).

    In futuro, poni in questo forum le domande relative a questi linguaggi.

    Ciao!
    MARCO BREVEGLIERI
    Software and Web Developer, Teacher and Consultant

    Home | Blog | Delphi Podcast | Twitch | Altro...

  3. #3
    Utente di HTML.it L'avatar di Stoicenko
    Registrato dal
    Feb 2004
    Messaggi
    2,254
    ovviamente prestando attenzione alle problematiche del multi-thread cioè stando attenti a non accedere mai contemporaneamente a risorse condivise.
    in lettura il problema non si pone, in scrittura si, è buona abitudine usare oggetti lock o semafori (mutex) per gestire le risorse condivise (e usare la parola chiava "volatile" in tali variabili)

    In teoria qualsiasi oggetto istanziato in un thread è accessibile dagli altri visto che l'area di memoria è condivisa, giusto?
    se intendi dire che nel corpo del thread (dowork) qualsiasi thread accede alle variabili (locali) create da qualsiasi altro thread allora no.. chiarisci questo punto.

    2) Quando un thread lancia un evento in pratica è come richiamare un delegate cioè un puntatore a funzione: il thread che lancia l'evento salterà dal flusso di istruzioni in cui si trova a quello della funzione che gestisce l'evento e una volta finito riprenderà l'esecuzione del flusso dalla riga successiva a quella che ha scatenato l'evento. Il meccanismo non è quindi asincrono bensì sincrono. Corretto? Inoltre la chiamata dell'evento, e quindi la chiamata della funzione che lo gestisce, comporterà il caricamento nello stack del thread in questione di una copia delle istruzioni da eseguire per cui nel caso in questione avremo potenzialmente N stack (relativi agli N thread) in cui contemporaneamente vi sarà caricata una copia della funzione LogManagement(). Siete d'accordo?
    corretto.. per averlo asincrono esistono altri metodi..

    3) A questo punto l'aspetto delicato sarà gestire eventuali accessi da parte di tutte queste repliche parallele del LogManagement() a membri di OBJ2 (che saranno condivisi in quanto l'oggetto è uno solo) o ad esempio ad un file TXT dove si va a scrivere. Siete d'accordo?
    esistono apposta i blocchi "lock" e i semafori (mutex) per fare ciò


    Alla luce di queste considerazioni sarei portato a chiedermi che differenza ci sarebbe se invece che usare gli eventi io passassi ad ogni costruttore di OBJ1 (dal thread principale) il puntatore all'oggetto OBJ2 e potessi quindi poi richiamare da ogni thread secondario il metodo LogManagement() direttamente.
    tecnicamente non cambia molto, la gestione ad eventi ti semplifica solamente le cose (in questo caso) e ti permette di sapere sempre chi è il thread che ha eseguito la funzione

  4. #4
    Utente di HTML.it
    Registrato dal
    May 2005
    Messaggi
    40
    Originariamente inviato da Stoicenko
    in lettura il problema non si pone, in scrittura si, è buona abitudine usare oggetti lock o semafori (mutex) per gestire le risorse condivise (e usare la parola chiava "volatile" in tali variabili)
    Si ok, non lo avevo spacificato ma pensavo appunto a strumenti di questo tipo

    se intendi dire che nel corpo del thread (dowork) qualsiasi thread accede alle variabili (locali) create da qualsiasi altro thread allora no.. chiarisci questo punto.
    intendo dire che se io ho N thread e questi utilizzano la stessa area di memoria (parto da questo punto fermo, se sbaglio ditemelo per favore) chiunque di questi istanzi degli oggetti (intendo istanze di classi definite a livello di progetto) potrà vedere questi ultimi raggiunti da altri thread, se questi ne conoscono la locazione. Non è così? questa è più una perplessità teorica che una esigenza pratica di questo lavoro.

    corretto.. per averlo asincrono esistono altri metodi..
    Ok, quindi scatenare un evento è di fatto come chiamare una funzione, con la differenza che si passa via delegate. Quindi ciascun chiamante (parlo di thread) avrà la sua copia in stack e andrà in parallelo MA se tale metodo va a scrivere membri dell'istanza (che è unica e non n-uplicata) a cui appartiene allora questo meccanismo in parallelo andrà ovviamente gestito con lock etc.

    esistono apposta i blocchi "lock" e i semafori (mutex) per fare ciò
    Ok

    tecnicamente non cambia molto, la gestione ad eventi ti semplifica solamente le cose (in questo caso) e ti permette di sapere sempre chi è il thread che ha eseguito la funzione
    Ok, quindi ricapitolando...se ho N thread e voglio accentrare la logica di gestione degli errori in una classe (mi pare il procedimento più pulito) posso fare le seguenti cose: (uso gli stessi nomi di esempio usati nel mio primo intervento)
    1) mi creo un oggetto OBJ2 dedicato alla gestione errori con un metodo base, ad es errorManagement(), tale metodo deve tener conto della possibilità di girare in N copie in parallelo per cui tramite lock etc sto attento a non scrivere contemporaneamente su risorse condivise. Definisco un delegate a cui errorMangement sia compatibile. Definisco in ogni OBJ1 un evento sendErr(). A livello di main associo ad ogni evento degli OBJ1 l'handler errorMangement(). Lancio i vari thread associati al metodo DoWork() di ogni OBJ1 e il gioco è fatto.
    2) Ad ogni OBJ1 passo l'oggetto (il puntatore) OBJ2 e poi a quel punto ogni thread secondario potrà lanciare direttamente il metodo errorManagement() accedendo al membro di OBJ1 che conserva il puntatore all'oggetto OBJ2 (anche qui si tratta di una chiamata ad un metodo per cui ogni thread avrà il proprio stack e la propria copia di funzione da eseguire)
    3) Definisco un delegate a livello di trhead principale, lo associo al metodo errorManagement() di OBJ2 e poi passo il delegate (quindi il suo puntatore) a tutti gli OBJ1 (che se lo salvano). Ogni thread secondario in esecuzione potrà quindi chiamare il delegate puntando così in automatico alla funzione errorMangement() di cui verrà caricata la copia nello stack locale e quindi eseguita con i soliti accorgimenti per le risorse condivise.
    4) Soluzione incerta: definisco a livello di main un evento sendError che poi passo ad ogni OBJ1 così da definirne uno unico per tutti...si può? O definendo l'evento a livello di main non è accessibile poi dai vari OBJ1? Non vedo problemi legati al fatto che siamo in thread diversi ma piuttosto forse problemi legati all'accessibilità di un evento da oggetti diversi...

    Ok, per ora è tutto...grazie per tutte le considerazioni che vorrete fare

  5. #5
    Originariamente inviato da Stoicenko
    in lettura il problema non si pone
    Tecnicamente il problema si pone anche in lettura se l'operazione di scrittura su ciò a cui stai accedendo non è atomica; il thread che legge il valore potrebbe infatti trovarlo in uno stato inconsistente, di passaggio tra uno stato consistente e un altro. Un esempio semplice sono gli interi a 64 bit sulle architetture a 32 bit: un'assegnazione di un valore a 64 bit ad un altro non è un'operazione atomica, ma avviene in due passaggi, per cui il thread che sta scrivendo il valore potrebbe essere interrotto quando ha scritto solo i primi 32 bit, ed un eventuale thread lettore che subentrasse in questo momento leggerebbe un valore che è una via di mezzo tra quello di prima e quello aggiornato.
    Amaro C++, il gusto pieno dell'undefined behavior.

  6. #6
    Utente di HTML.it
    Registrato dal
    May 2005
    Messaggi
    40
    Originariamente inviato da MItaly
    Tecnicamente il problema si pone anche in lettura se l'operazione di scrittura su ciò a cui stai accedendo non è atomica; il thread che legge il valore potrebbe infatti trovarlo in uno stato inconsistente, di passaggio tra uno stato consistente e un altro. Un esempio semplice sono gli interi a 64 bit sulle architetture a 32 bit: un'assegnazione di un valore a 64 bit ad un altro non è un'operazione atomica, ma avviene in due passaggi, per cui il thread che sta scrivendo il valore potrebbe essere interrotto quando ha scritto solo i primi 32 bit, ed un eventuale thread lettore che subentrasse in questo momento leggerebbe un valore che è una via di mezzo tra quello di prima e quello aggiornato.
    Ok, questo aspetto mi è chiaro. Forse Stoicenko intendeva dire se si ha uno scenario in cui più thread accedono soltanto in lettura e mai in scrittura.

    Invece riguardo a questo:
    Ok, quindi ricapitolando...se ho N thread e voglio accentrare la logica di gestione degli errori in una classe (mi pare il procedimento più pulito) posso fare le seguenti cose: (uso gli stessi nomi di esempio usati nel mio primo intervento)
    1) mi creo un oggetto OBJ2 dedicato alla gestione errori con un metodo base, ad es errorManagement(), tale metodo deve tener conto della possibilità di girare in N copie in parallelo per cui tramite lock etc sto attento a non scrivere contemporaneamente su risorse condivise. Definisco un delegate a cui errorMangement sia compatibile. Definisco in ogni OBJ1 un evento sendErr(). A livello di main associo ad ogni evento degli OBJ1 l'handler errorMangement(). Lancio i vari thread associati al metodo DoWork() di ogni OBJ1 e il gioco è fatto.
    2) Ad ogni OBJ1 passo l'oggetto (il puntatore) OBJ2 e poi a quel punto ogni thread secondario potrà lanciare direttamente il metodo errorManagement() accedendo al membro di OBJ1 che conserva il puntatore all'oggetto OBJ2 (anche qui si tratta di una chiamata ad un metodo per cui ogni thread avrà il proprio stack e la propria copia di funzione da eseguire)
    3) Definisco un delegate a livello di trhead principale, lo associo al metodo errorManagement() di OBJ2 e poi passo il delegate (quindi il suo puntatore) a tutti gli OBJ1 (che se lo salvano). Ogni thread secondario in esecuzione potrà quindi chiamare il delegate puntando così in automatico alla funzione errorMangement() di cui verrà caricata la copia nello stack locale e quindi eseguita con i soliti accorgimenti per le risorse condivise.
    4) Soluzione incerta: definisco a livello di main un evento sendError che poi passo ad ogni OBJ1 così da definirne uno unico per tutti...si può? O definendo l'evento a livello di main non è accessibile poi dai vari OBJ1? Non vedo problemi legati al fatto che siamo in thread diversi ma piuttosto forse problemi legati all'accessibilità di un evento da oggetti diversi...
    che mi dite?

  7. #7
    Utente di HTML.it
    Registrato dal
    May 2005
    Messaggi
    40
    Aggiungo ancora una domanda/considerazione.

    Io qui ho proposto uno scenario multi-thread generico per poi pormi problemi relativi agli eventi e alla loro esecuzione sincrona (così abbiamo concluso).
    Nel mio caso non avevo problematiche legate a controlli in una windows form e alla impossibilità di accedervi se non dal thread principale in cui è istanziata appunto la classe form contenente il resto.
    Ma calandoci in uno scenario simile:

    se nel mio main dentro la mia classe form definisco un oggetto OBJK con un metodo DoWork() e 2 eventi (sendMessage e completed) , poi nella mia classe form definisco anche un paio di metodi onMessage e onCompleted e associo tali metodi ai due eventi di OBJK.
    Ecco, poi dal main dopo aver fatto queste cose lancio un thread secondario che esegue il metodo DoWork() di OBJK.
    Supponiamo ora che tale metodo voglia andare a scrivere in una label contenuta nel Form.
    1) Se provo ad accedere direttamente dal mio DoWork() al label control faccio una operazione generalmente illecita poichè per i control il fatto che l'area di memoria sia condivisa da tutti i thread in gioco poco importa ed invece di lasciare allo sviluppatore la possibilità di gestire gli accessi ai control con dei lock etc...viene direttamente negata la possibilità di accesso se non al thread da cui sono stati istanziati tali control (cioè quello che contiene il Form, quindi ll principale). Poi si può disabilitare questa restrizione etc...ma in generale diciamo che la situazione è questa, giusto?
    2) Passo per il mio evento sendMessage: così facendo il mio thread secondario va ad eseguire il metodo onMessage il quale accede al label control. E' lecito? sembrerebbe sempre un accesso da parte del thread secondario visto che è lui che esegue l'handler dell'evento che scatena...unica differenza dal caso 1 è che ora il metodo è definito nell'oggetto contenente il control. Come mai allora così va bene? E' esattamente quello che faccio quando uso un backgroungworker o sbaglio?

  8. #8
    Utente di HTML.it L'avatar di Stoicenko
    Registrato dal
    Feb 2004
    Messaggi
    2,254
    si ad entrambe..

    infatti ad un evento è sempre associato un delegate..
    l'unico dubbio è che non vada eseguito direttamente il delegate invece che usare l'evento.

    ad esempio:

    thread1 -> evento -> metodo associato (delegate)
    thread1 -> delegate tramite invoke

    la seconda scelta è quella canonica, credo funzioni anche la prima (essendoci in mezzo cmq il delegate) i miei dubbi sorgono solo per il tipo di invocazione una "diretta" e l'altra fortemente "delegata" all'oggetto di invocazione

  9. #9
    Utente di HTML.it
    Registrato dal
    May 2005
    Messaggi
    40
    ...quindi concordi che via evento (che poi in pratica significa via delegate ma con un livello di astrazione più alto) posso intervenire su un control del form da un thread secondario, anche se l'handler è comunque eseguito dal thread secondario, da cui non si dovrebbe/potrebbe accedere al control in questione. Però se le cose stanno così non mi tornano bene i conti...nel senso che partiamo con lo studiare una soluzione che ci consenta di accedere ad un control da un thread secondario senza farlo direttamente (non è consentito) e poi ci è concesso di farlo via evento..cioè via delegate...cioè lanciando una funzione che comunque eseguiamo nel nostro thrread secondario. Non è contraddittoria come cosa?

    Altra osservazione...Stoicenko, hai parlato di invoke del delegate...intendi utilizzare il control.invoke() ?
    Scusami se mi soffermo su ogni cosa ma mi sto documentando e vorrei mi tornassero tutti i conti..

  10. #10
    Utente di HTML.it L'avatar di Stoicenko
    Registrato dal
    Feb 2004
    Messaggi
    2,254
    guarda.. se non erro ho riscontrato dei problemi con gli eventi.. devo fare delle prove ma direi che è meglio un delegato..

    si parlo del control.invoke, anche se ci sono metodi per usare invoke anche senza un controllo

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.