2010-09-25 7 views
19

So che se sto modificando un controllo da un thread diverso, dovrei fare attenzione perché WinForms e WPF non consentono di modificare lo stato del controllo da altri thread.Perché solo il thread dell'interfaccia utente è autorizzato a modificare l'interfaccia utente?

Perché è presente questa restrizione?

Se riesco a scrivere codice thread-safe, dovrei essere in grado di modificare lo stato del controllo in modo sicuro. Allora perché questa restrizione è presente?

+0

Credo che una delle ragioni per cui esiste questa limitazione sia che non ci sia sempre né più né meno di un thread garantito per ascoltare il ciclo dei messaggi di Windows. –

+1

@Tim: non è giusto, possono esistere più thread dell'interfaccia utente, ognuno con il proprio loop di messaggi. Ad esempio, è possibile ruotare un secondo thread per visualizzare una finestra di dialogo della barra di avanzamento. –

+0

@ Ben: questo post è contrassegnato con C#. In un'applicazione .Net, è necessario sempre eseguire il marshalling del controllo sul thread della GUI per eseguire il lavoro relativo al disegno (o comportare un comportamento erratico o un'eccezione). Naturalmente, ciò non impedisce di generare un numero qualsiasi di thread per eseguire operazioni di background. Per quanto riguarda l'ascolto degli effettivi messaggi Win32 in arrivo, l'unico modo che conosco per farlo (a parte una chiamata esterna) è con "protected override void WndProc (ref Message m)". In teoria, potrebbe prendere i messaggi passati a questo metodo e usarli su qualsiasi thread. –

risposta

19

Diversi framework GUI presentano questa limitazione. Secondo il libro Java Concurrency in Practice la ragione di ciò è evitare il blocco complesso. Il problema è che i controlli della GUI potrebbero dover reagire a entrambi gli eventi dall'interfaccia utente, dall'associazione dati e così via, che porta al blocco da diverse fonti diverse e quindi al rischio di deadlock. Per evitare questo .NET WinForms (e altre interfacce utente) limita l'accesso ai componenti a un singolo thread e quindi evita il blocco.

+0

Aggiungete a ciò l'interoperabilità con i controlli ActiveX (controllo di Brower ecc. - eventualmente indirettamente tramite l'integrazione di winforms) e avete anche una legacy COM che è Single Threaded Appartement. – TomTom

+1

Windows ** non ** limita l'accesso al thread proprietario. Questa limitazione è introdotta da .NET. –

+0

@ Ben: non lo sapevo. Aggiornerò la mia risposta per riflettere questo. Controllerò cosa dice il libro. –

3

.NET si riserva il diritto di accedere al controllo dell'utente nel thread in cui è stato creato in qualsiasi momento. Pertanto gli accessi che provengono da un altro thread non possono mai essere thread-safe.

8

Nel caso di finestre, quando viene creato un controllo, gli aggiornamenti dell'interfaccia vengono eseguiti tramite messaggi da un messaggio pompa. Il programmatore non ha il controllo diretto del filo su cui è in funzione la pompa, pertanto l'arrivo di un messaggio per un controllo potrebbe comportare la modifica dello stato del controllo. Se un altro thread (di cui il programmatore era in controllo diretto) fosse autorizzato a modificare lo stato del controllo, sarebbe necessario mettere in atto una sorta di logica di sincronizzazione per impedire il danneggiamento dello stato del controllo. I controlli in .Net non sono thread-safe; questo è, sospetto dal design. Mettere la logica di sincronizzazione in tutti i controlli sarebbe costoso in termini di progettazione, sviluppo, test e supporto del codice che fornisce questa funzionalità. Il programmatore potrebbe naturalmente fornire sicurezza di thread al controllo per il proprio codice, ma non per il codice che si trova in .Net che è in esecuzione contemporaneamente al suo codice. Una soluzione a questo problema consiste nel limitare questi tipi di azioni a un solo thread e a un solo thread, il che semplifica la manutenzione del codice di controllo in .Net.

1

Potrebbe essere possibile creare il proprio codice thread-safe, ma non è possibile in alcun modo inserire le primitive di sincronizzazione necessarie nel codice WinForm e WPF incorporato che corrispondono a quelli del codice. Ricorda, ci sono un sacco di messaggi che vengono trasmessi dietro le quinte che alla fine fanno sì che il thread dell'interfaccia utente acceda al controllo senza che tu te ne accorga realmente.

Un altro aspetto interessante di un'affinità del thread di controlli è che esso potrebbe (sebbene sospetto che non lo farebbero mai) utilizzare il modello Thread Local Storage. Ovviamente, se si accede a un controllo su un thread diverso da quello su cui è stato creato, non sarebbe possibile accedere ai dati TLS corretti, a prescindere da quanto attentamente il codice sia stato strutturato per proteggersi da tutti i normali problemi del codice multithread.

1

In realtà, per quanto ne so, è stato il piano dall'inizio! È possibile accedere a ogni controllo da qualsiasi thread! E solo perché il blocco dei thread era necessario quando un altro thread richiedeva l'accesso al controllo - e poiché il blocco è costoso - è stato creato un nuovo modello di thread chiamato "noleggio di thread". In quel modello, i controlli correlati sarebbero aggregati in "contesti" usando solo un thread, riducendo così la quantità di blocco necessaria. Molto carino, eh?

Purtroppo, quel tentativo era troppo audace per avere successo (e un po 'più complesso, perché il blocco era ancora necessario), in modo che il buon vecchio Windows Form threading modello with il singolo thread dell'interfaccia utente e con il filo creazione di rivendicare la proprietà dei il controllo-- è usato ancora una volta in wPF per rendere le nostre vite ... più facili?

1

Windows supporta molte operazioni che, soprattutto utilizzate in combinazione, non sono intrinsecamente thread-safe. Cosa dovrebbe accadere, ad esempio, se un thread sta tentando di inserire del testo in un campo di testo che inizia con il 50esimo carattere, mentre un altro thread tenta di eliminare i primi 40 caratteri da quel campo? È possibile che Windows utilizzi i blocchi per garantire che la seconda operazione non possa essere avviata fino al completamento della prima, ma l'utilizzo di blocchi aggiungerebbe un sovraccarico a ogni operazione e aumenterebbe la possibilità di deadlock se le azioni su una entità richiedono manipolazione di un altro. Richiedere che le azioni che coinvolgono una particolare finestra debbano avvenire su un particolare thread è un requisito più stringente di quanto sarebbe necessario per impedire che combinazioni non sicure di operazioni vengano eseguite simultaneamente, ma è relativamente facile da analizzare. Utilizzare i controlli da più thread ed evitare scontri con altri mezzi sarebbe generalmente più difficile.

Problemi correlati