Operazione cross-thread non valida

di il
11 risposte

Operazione cross-thread non valida

Buongiorno a tutti.
Come da titolo sono incappato in questo errore.
Sto cercando di sviluppare un progetto di tipo DLL che stia sempre in ascolto su un socket (ma potrebbe essere anche una seriale) e che inneschi un evento quando arriva un pacchetto prestabilito da una unità esterna connessa appunto in Ethernet o seriale.
Per fare ciò ho pensato di fare il tutto in un thread apposito fatto come di seguito (ho tralasciato i dettagli implementativi che penso non servano)

Nella classe ho dichiarato:

        private Thread ReadTcpData;                                                             //thread lettura dati TCP
        public delegate void DataReceivedEventHandler(TcpThreadExch thd);
        public event DataReceivedEventHandler DataReceived;	
Nel costruttore della classe:
nota: thrTcp è una classe usata per lo scambio dati con il thread internamente

            ReadTcpData = new Thread(ThreadReadTcpData);                            //istanzio il thread lettura dati TCP
            ReadTcpData.Start(thrTcp);
Ed il thread è così:

                do
                {
                     try
                    {
                        while ((nbrec = stream.Read(btrec, 0, btrec.Length)) != 0)  //fino a che ho dei byte nel buffer di ricezione
                        {
			   //...
                           // qui c'è tutto il codice che si occupa di ricevere ed analizzare i dati (in questo caso da un socket)
                           // tcpthr è una classe che utilizzo per lo scambio dati del thread
                           //...
                           tcpthr.recDataString = Encoding.ASCII.GetString(dataRec.ToArray());
                           DataReceived.Invoke(tcpthr);
                        }
                    }
                    catch (Exception ex)
                    {
                        //qui gestirò gli errori
                    }

                    Thread.Sleep(100);
                } while (!tcpthr.exit);			//qui esco quando serve
Da una applicazione windows form poi istanzio la mia DLL e creo il metodo per l'evento

        private void Form1_Load(object sender, EventArgs e)
        {
            tcps = new TcpServer("127.0.0.1", 11000, 512);

            tcps.DataReceived += new TcpServer.DataReceivedEventHandler(Tcp_DataReceived);
        }
        
        protected void Tcp_DataReceived(TcpThreadExch dataReceived)
        {
            string pippo = dataReceived.recDataString;
            try
            {
                label1.Text = pippo;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
Ora, l'evento si innesca e nella variabile pippo ho effettivamente la stringa dei dati letti, ma quando cerco di scriverla in una label ottengo l'errore nel titolo. Evidentemente non ho capito come fare un thread "thread safe" usando correttamente i delegate...
Potete aiutarmi a capire?

Grazie

11 Risposte

  • Re: Operazione cross-thread non valida

    È un errore in cui penso incappino tutti.
    La parte grafica deve essere aggiornata separatamente. Metti un timer a 100 ms e quando c'è il tick aggiorni a video label e tutto il resto con quello che è arrivato dal socket (vedi tu come organizzarti, ci sono molte strade)
  • Re: Operazione cross-thread non valida

    Intanto ti ringrazio per la risposta ultraveloce.
    Ma non c'è proprio un modo lato applicazione winform per utilizzare direttamente l'evento senza dover introdurre timer?
    Chiedo perchè sarebbe bello utilizzare la dll in modo "pulito" sfruttando l'evento come se fosse un qualsiasi altro.
  • Re: Operazione cross-thread non valida

    Puoi usare Invoke()
    https://docs.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-make-thread-safe-calls-to-windows-forms-controls
  • Re: Operazione cross-thread non valida

    Si ho letto, ma se volessi fare in modo che la mia dll fornisse già un evento nello stesso thread in cui gira il form nel quale è istanziata?
    Questo non per pignoleria ma perchè nelle applicazioni che la utilizzeranno mi piacerebbe solamente scrivere solamente il codice dell'evento.
  • Re: Operazione cross-thread non valida

    A parte che su tre soluzioni (anzi quattro - puoi disattivare il check sulle cross thread) non te ne è piaciuta una, tu vuoi fare una DLL che gestisca un socket TCP e che metta becco su un controllo di un form? E che dll è che mescola mele e arance?
  • Re: Operazione cross-thread non valida

    Allora... non è che non mi piacciono, sto semplicemente valutando il modo migliore. Provo a spiegarti:
    La ddl, che vorrei fare per poterla utilizzare in vari scenari, dovrebbe servire a non farmi occupare ogni voltadella gestione del socket con annessi e connessi.
    Immaginati un'applicazione che tiene sott'occhio dei dati provenienti da impianti. Bene, mi piacerebbe istanziare la mia dll fornendogli: indirizzo ip, porta e quant'altro serve nello specifico.
    E sapendo che questa dll mi scatena un evento quando arrivano dei dati, preoccuparmi di gestire solamente il codice dell'evento.
    Ovviamente i dati che arrivano, oltre che ad essere utilizzati dall'applicazione, magari voglio anche visualizzarli in una semplice label per monitorarli visivamente. Quindi non è che voglio mescolare mele e arance...
  • Re: Operazione cross-thread non valida

    Prova a fare in questo modo:
    label1.Invoke(new Action(delegate () {
    	label1.Text = pippo;
    }));
  • Re: Operazione cross-thread non valida

    @Lucios
    Visto il tipo di applicazione, secondo me sei sulla strada sbagliata. La tua interfaccia grafica diventerà lentissima se ogni volta che arriva qualcosa sulla rete vai ad aggiornare i controlli. Ma sei libero di sperimentare da solo a tal proposito

    @peo
    Invoke era appunto una della alternative proposte
  • Re: Operazione cross-thread non valida

    Visto il tipo di applicazione, secondo me sei sulla strada sbagliata. La tua interfaccia grafica diventerà lentissima se ogni volta che arriva qualcosa sulla rete vai ad aggiornare i controlli. Ma sei libero di sperimentare da solo a tal proposito
    No perchè non è una rete nella quale girano miliardi di pacchetti, semplicemente le apparecchiature connesse mandano dei dati non sollecitati solo in determinate occasioni, ad es. per un cambio di stato.

    @Peo
    Se non c'è altro modo farò così.
  • Re: Operazione cross-thread non valida

    Lucios ha scritto:


    Se non c'è altro modo farò così.
    io uso questo sistema da anni. credo che sia il più semplice e 'pulito'.

    Per quanto riguarda le prestazioni, nei casi in cui so che l'aggiornamento sarà molto frequente, imposto un tempo minimo tra un aggiornamento e il successivo utilizzando Environment.TickCount.

    Cioè una cosa del genere:
    private long lastTicks = 0;
    protected void Tcp_DataReceived(TcpThreadExch dataReceived) {
    	if (lastTicks == 0 || (Environment.TickCount - lastTicks) > 1000) {
    		// aggiorna la label solo se è trascorso più di un secondo dall'ultima volta
    		label1.Invoke(new Action(delegate () {
    			label1.Text = pippo;
    		}));
    		lastTicks = Environment.TickCount;
    	}
    }
    
  • Re: Operazione cross-thread non valida

    @Peo
    si certo, così funziona.

    interessante il TickCount, me lo segno.

    Grazie mille
Devi accedere o registrarti per scrivere nel forum
11 risposte