Domanda

Ho un PropertyGrid nella mia applicazione che viene utilizzata per modificare oggetti arbitrari.Devo essere in grado di eseguire una subroutine arbitraria su un altro thread che esamini anche questi oggetti (funzionalità di ricerca, se sei curioso).Il problema ovvio è che un utente potrebbe modificare uno di questi oggetti nello stesso momento in cui il mio thread di ricerca lo sta leggendo, cosa che sarebbe preferibile evitare (anche se probabilmente non risulterà in nulla di critico poiché il mio thread di ricerca sta semplicemente leggendo, non scrivere).

Chiamando lock(obj) è abbastanza semplice dal mio thread di ricerca, ma dopo aver esaminato la documentazione e una breve occhiata al codice PropertyDescriptorGridEntry in Reflector, non riesco a trovare un punto analogo per utilizzare un System.Threading.Monitor.Enter()/Exit() chiamare l'oggetto in questione su PropertyGrid.Speravo che ci fossero eventi BeginEdit ed EndEdit che rendessero tutto abbastanza semplice, ma non riesco a trovare nulla del genere.Preferirei non bloccare l'intero oggetto mentre è visualizzato in PropertyGrid poiché ciò bloccherebbe ovviamente il mio thread di ricerca finché non verrà selezionato un altro oggetto.

Sono un po' nuovo nel modello di threading di Windows Forms, quindi spero che ci sia qualche risposta ovvia che ho appena trascurato.Qualsiasi aiuto?

Modificare: Clonare in modo sincrono i miei oggetti prima di eseguire la ricerca in modo asincrono sarà probabilmente abbastanza inefficiente da permettermi di eseguire la ricerca stessa in modo sincrono: lo scopo dell'esecuzione in modo asincrono è ovviamente quello di consentire ai miei utenti di continuare a lavorare mentre la ricerca è in esecuzione.La ricerca deve essere ben scalata, poiché il set di dati che sto esaminando finirà per essere arbitrariamente grande, il che fa sembrare che la clonazione sincrona causerà il problema di usabilità che sto cercando di evitare.

È stato utile?

Soluzione

Penso che si sta andando ad avere a che fare un po 'di lavoro per questo.

In primo luogo, si dovrà creare un'implementazione di ICustomTypeDescriptor che basare la sua implementazione su qualsiasi TypeDescriptor si sarebbe normalmente ottenere per quel tipo.

Poi, nelle implementazioni dei metodi che espongono i descrittori dei membri che si desidera agganciare, si dovrebbero fornire sottoclassi che derivano da tali descrittori e sovrascrivere i metodi appropriati per avvolgere un lock attorno.

Quindi, per una proprietà, si dovrebbe implementare GetProperties restituire i vostri sottoclassi specifiche di PropertyDescriptor. Queste sottoclassi avrebbero sovrascrivere i metodi GetValue e SetValue (e altri) e utilizzare un blocco quando si accede a queste variabili.

Si segnala che il blocco come questo nel thread UI è probabilmente una cattiva idea, dal momento che non si vuole bloccare arbitrariamente operazioni su quel thread. Potrebbe essere meglio per creare solo un clone dell'oggetto e quindi avere un metodo che aggiorna le vendite di oggetti quando si è fatto.

Altri suggerimenti

Questo sarà infatti non essere thread-safe. Si potrebbe maresciallo bit del codice di ricerca al thread dell'interfaccia utente, ma che rallentare le cose, e forse sconfiggere il punto della filettatura ...

In quanto tempo fa la ricerca ha bisogno di essere? Si può lavorare contro un clone? etc

Si può clonare i dati prima di visualizzarlo, e ottenere il tuo thread di ricerca per lavorare sul clone? Se si desidera che il thread di ricerca per "vedere" le modifiche, si potrebbe rispondere a eventi sul PropertyGrid e magari fare controllato le modifiche al clone in qualche modo. (E 'probabilmente più facile usare solo i dati "stantio", anche se.)

So che la clonazione dei dati suona terribilmente inefficiente, ma filettatura certamente più semplice quando ogni filo lavora su dati completamente indipendenti (o tutti i fili di sola lettura).

Potrei sbagliarmi, ma mi sembra che tu venga dalla parte sbagliata.

Ci sono due questioni distinte che devi affrontare.

Innanzitutto, assicurandosi che si possa accedere a PropertyGrid solo sul thread dell'interfaccia utente.Se si accede a uno qualsiasi dei suoi metodi (inclusi getter/setter di proprietà) da altri thread, soffrirai dolore in modi misteriosi.Le eccezioni sono InvokeRequired() E Invoke, Ovviamente.

In secondo luogo, assicurati che il thread di ricerca possa funzionare correttamente.

Per risolvere il primo problema, O assicurati che i tuoi oggetti non vengano mai modificati tranne dal thread dell'interfaccia utente, O rendere consapevoli tutti i thread di attivazione degli eventi in modo che gli eventi degli oggetti (come PropertyChanged) vengano attivati ​​solo sul thread dell'interfaccia utente.

Il secondo problema è più semplice: finché il thread di ricerca LEGGE solo dai tuoi oggetti principali, tutto dovrebbe funzionare correttamente.Sì, il tuo thread di ricerca potrebbe inavvertitamente visualizzare alcuni dati parzialmente aggiornati, ma è davvero un problema?

Un paio di pensieri finali...

  • Non implementare la ricerca per scorrere il PropertyGrid stesso;ottenere un elenco degli oggetti in anticipo (non è necessario che siano cloni) e lavorarci invece.

  • Hai considerato l'utilizzo di Idle Processing per eseguire la ricerca?IL Application L'oggetto attiva un evento ogni volta che l'applicazione esaurisce i messaggi da elaborare: puoi collegarti a questo ed eseguire 1 passaggio della ricerca ogni volta che l'evento viene attivato.Una specie di threading da poveri, ma senza i grattacapi mutex/lock/semafono.L'ho usato con ottimi risultati in passato.

Aggiornamento:Se ricordo bene, PropertyGrid rispetta il IEditableObject interfaccia, chiamata BeginEdit non appena inizi a modificare una riga e EndEdit quando ti sposti su una riga diversa.Potresti sfruttare questo per evitare che il tuo thread di ricerca veda modifiche incomplete.

Aggiornamento 2:Successivamente, ho scoperto quello che già sapevi: PropertyGrid no rispettare l'interfaccia IEditableObject.

Ho un altro suggerimento, anche se potrebbe richiedere più lavoro di quanto desideri investire.

Nel periodo in cui lo spazio dei nomi System.Transactions è stato introdotto in .NET 2.0, ho visto un articolo sull'utilizzo della transazione di ambiente per fornire l'isolamento dei thread per gli oggetti.L'idea è che le proprietà dell'oggetto abbiano una doppia archiviazione.Per prima cosa hai il valore impegnato, visibile a tutti i thread, e hai una variabile locale del thread utilizzata per memorizzare i valori non impegnati in base al thread.Quando una proprietà viene modificata, l'oggetto si inserisce in qualsiasi transazione di ambiente memorizzando il nuovo valore nel thread locale.Quando la transazione esegue il commit o il rollback, il valore del thread viene archiviato o eliminato.

Sfortunatamente, non riesco a trovare l'articolo originale, anche se sembra che CSLA fornisca questo supporto.Spero che questo ti aiuti.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top