2012-10-14 11 views
6

Sto migliorando un'applicazione (Win64, C++) rendendola più asincrona. Sto usando Concurrency Runtime e ha funzionato benissimo per me finora.Implementazione di variabili locali del task per Concurrency Runtime

L'applicazione esegue fondamentalmente un numero di "lavori" che trasformano i dati. Per tenere traccia di ciò che fa ciascun lavoro, alcuni sottosistemi sono dotati di codice per tracciare determinate operazioni eseguite dal lavoro. In precedenza, si utilizzava una singola variabile globale che rappresenta il lavoro attualmente in esecuzione per poter registrare le informazioni di tracciamento senza dover passare le informazioni di contesto fino in fondo alla catena chiamante. Ogni lavoro può anche girare usare ConcRT per parallelizzare il lavoro stesso. Tutto funziona abbastanza bene.

Ora, sto refactoring dell'applicazione in modo che possiamo eseguire i lavori di primo livello in parallelo. Ogni lavoro viene eseguito come attività ConcRT e funziona bene per tutti i lavori tranne quelli che necessitano di tracciamento.

Ciò di cui ho fondamentalmente bisogno è un modo per associare alcune informazioni di contesto a un'attività e avere tale flusso per qualsiasi altro compito generato da tale attività. Fondamentalmente, ho bisogno di variabili "Task Local".

Con ConcRT non è possibile utilizzare semplicemente i thread locals per archiviare le informazioni di contesto, poiché il lavoro può generare altri lavori utilizzando ConcRT e questi verranno eseguiti su qualsiasi numero di thread.

Il mio attuale approccio prevede la creazione di un certo numero di istanze di Scheduler all'avvio e la generazione di ogni lavoro in un programmatore dedicato a quel lavoro. Posso quindi utilizzare la funzione Concurrency::CurrentScheduler::Id() per recuperare un numero intero che posso usare come chiave per capire il contesto. Questo funziona, ma il single-step attraverso il Concurrency::CurrentScheduler::Id() in assembly mi rende un po 'smorzato dal momento che esegue più chiamate di funzioni virtuali e controlli di sicurezza che aggiunge un bel po' di overhead, che è un po 'un problema dal momento che questa ricerca deve essere fatta in un alto tasso in alcuni casi.

Quindi - c'è un modo migliore per realizzare questo? Mi sarebbe piaciuto avere un meccanismo TaskLocal/userdata di prima classe che mi permettesse di associare un singolo puntatore di contesto con l'attuale Scheduler/SchedulerGroup/Task che potrei recuperare con un sovraccarico molto ridotto.

Un hook che viene chiamato ogni volta che un thread ConcRT afferra una nuova attività sarebbe il mio ideale, in quanto potrei quindi recuperare l'ID Scheduler/ScheduleGroup e memorizzarlo in un thread locale per un sovraccarico minimo di accesso. Ahimè, non riesco a vedere alcun modo per registrare un tale hook e non sembra possibile implementare le classi Custom Scheduler per PPL/agenti (vedi this article).

risposta

0

C'è qualche motivo per cui non è possibile passare una sorta di oggetto contesto a queste attività che fornisce loro un'interfaccia per l'aggiornamento del loro stato? Perché da dove mi trovo, sembra che tu abbia un problema davvero brutto con Singletons (alias variabili globali), uno che dovrebbe essere risolto con un'iniezione di dipendenza.

Se l'iniezione di dipendenza non è un'opzione, esiste un'altra strategia per trattare con Singletons. Questa strategia sta sostanzialmente consentendo a Singleton di essere una "pila". Puoi 'spingere' un nuovo valore sul Singleton, e quindi tutti quelli che vi accedono ottengono questo nuovo valore. E poi puoi "far scattare" il valore indietro e il valore prima di premere viene ripristinato. Questo non deve essere modellato direttamente con uno stack effettivo, motivo per cui inserisco le parole "push", "pop" e "stack" tra virgolette.

È possibile adattare questo modello alla circostanza disponendo di un thread Singleton locale inizializzato con il valore (non l'intera pila di valori, solo il valore superiore) della versione del thread padre di questa variabile. Quindi, se per questo thread e i suoi figli è richiesto un nuovo contesto, è possibile inserire un nuovo valore sul Singleton locale del thread.

+0

Sì, questa è la soluzione più ovvia, ed è fondamentalmente ciò che facciamo per la maggior parte del nostro codice. Tuttavia, per un sottoinsieme di codice, non è un'opzione in quanto porterebbe a un'eccessiva prolissità. Fondamentalmente, abbiamo classi di oggetti dati generate dal codice con setter/getter. I getter possono anche essere strumentati per registrare che sono stati utilizzati, per tenere traccia delle dipendenze dei dati. Sarebbe molto imbarazzante dover passare oggetti di contesto in tutti questi getter e canalizzare il contesto in tutte le catene di chiamata. L'utilizzo dell'ID scheduler per implementare sostanzialmente le variabili locali dello scheduler funziona, tuttavia, con buone prestazioni. –

+0

@StefanBoberg: esiste un modello alternativo per un Singleton che non è realmente un Singleton. Ti consente di "impilare" i valori. È possibile inserire un nuovo valore nello stack e questo diventa il valore di Singleton fino a quando il valore viene spuntato e il vecchio valore ripristinato. È possibile adattare questo al proprio ambiente avendo una variabile locale del thread che è stata inizializzata con il valore dell'istanza del thread principale di questa variabile. – Omnifarious