Errore nella gestione delle strategie in ambienti con multithreading

5

TL; DR Quali strategie di generazione e gestione degli errori si utilizzano nel codice multithreaded destinato all'uso da parte di altri e perché vengono utilizzati? Se applicabile, indica quale paradigma di programmazione è utile. Sono più interessato ad ambienti imperativi, concomitanti, ma tutti in generale saranno utili.

Sto scrivendo una piccola libreria di concorrenza che al momento è un progetto di apprendimento di C ++ 11 pet / ma che potrebbe essere utilizzata internamente dal mio lavoro più tardi. In termini di dominio è più nel regno di DSP e media streaming, ma dal momento che questo sarà utilizzato in un motore di gioco ho bisogno di una gestione degli errori abbastanza strong.

Il mio grande blocco al momento non mi dà la testa sul codice parallelo e sulle strutture dati, ma su come gestire e segnalare gli errori. La mia esperienza principale in sistemi di grandi dimensioni sono i giochi, ma di solito uso le librerie, non progettandole. Sto solo cercando strategie diverse e come potrebbero essere utilizzate in diverse situazioni in quanto questo è un divario piuttosto grande nelle mie conoscenze.

La mia più grande area di preoccupazione è l'utilizzo di una strategia in modo che se il programma può riprendersi, dovrebbe. Se si ripristina, ci dovrebbe essere un modo per informare un utente su cosa è successo attraverso un qualche tipo di meccanismo. Ho già alcune strutture dati che sebbene possano essere ripristinate, la memoria potrebbe essere trapelata se un distruttore fallisce, per esempio.

Alcuni approcci:

  • Gestire le eccezioni per le quali è possibile ripristinare in modo sicuro dall'interno della libreria, ma consentire alle eccezioni fatali di propagarsi agli utenti della libreria per indicare che l'oggetto si trova ora in uno stato indefinito. È il mio approccio preferito negli ambienti a thread singolo, ma questo approccio non comunicherà lo stato negativo ad altri thread.
  • Quando si verifica un'eccezione che inserisce una struttura di dati in uno stato irrecuperabile, elimina la struttura dei dati e imposta un flag per bloccare ulteriori operazioni su quella struttura di dati, quindi genera un'eccezione generale all'utente finale. Questo è difficile per gli algoritmi lock-free.
  • Inoltra uno stato di errore tramite calcoli paralleli. Funziona bene per le reti di processo Kahn e altri modelli di concorrenza di alto livello. Non è così utile se un primitivo che supporta il modello di alto livello ha fallito.
  • Termina il thread / attività che ha causato l'eccezione. Funziona bene per il thread di dati locali / computazione, ma non molto di una soluzione per i dati condivisi.

Come nota, so che una buona libreria probabilmente userà un mix di qualcosa in più rispetto a ciò che è elencato sopra. Semplicemente non ho l'esperienza per sapere quale strategia è buona per qualsiasi sistema sufficientemente grande.

    
posta BlamKiwi 27.08.2014 - 14:05
fonte

1 risposta

2

I miei due centesimi.

Innanzitutto, la maggior parte dei modelli asincroni che ho visto nelle librerie tendono a rendermi frustrato. Tutti sembrano avere la loro marca leggermente diversa di asincrono e molte di queste interfacce non sono buone. In quanto tale, tendo ad apprezzare le librerie che mantengono tutto sincronico. Tieni presente che i callback possono essere comunque validi. Ma mantieni tutta la logica su un thread; il programmatore dell'applicazione spesso desidera pensare al threading separato dal compito che la libreria sta cercando di eseguire.

In secondo luogo, una piccola libreria dedicata al codice asincrono può essere una cosa molto buona - purché sia il suo unico obiettivo. Un pattern che ho visto e apprezzato in C # è quello di concatenare azioni su thread diversi, ma di scriverlo in una singola modalità threaded con un'interfaccia fluente. (La nuova parola chiave await è un po 'sulla stessa lunghezza.) Un punto in comune in cui questo si presenta è in dispacci sul thread dell'interfaccia utente. Quindi fornire un modo per gestire le eccezioni alla fine, quasi come un blocco catch. Quindi per esempio forse qualcosa del genere:

...
int expensiveResult=-1;
YourThreadLibrary
  .Background(()=>expensiveResult=DoLongRunningTaskToCreate())
  .UI(()=>UpdateUI(expensiveResult))
  .Exception(ex=>LogIt(ex));

Le eccezioni sono la strada da percorrere in C # e hai GC, quindi la tua situazione potrebbe essere diversa. Ma il modello può ancora avere un senso.

So che questo potrebbe sembrare troppo semplicistico, ma questi sono gli strumenti che ho visto essere abbastanza generali da funzionare su molti problemi. La cosa bella è che un'interfaccia fluente come questa è sicuramente aperta all'estensione se scritta correttamente, quindi puoi aggiungere il tuo .ParallelFailOnAny (params Action []) ecc.

    
risposta data 28.08.2014 - 07:32
fonte