Il modo migliore per definire, avviare e interrompere i thread in C #

5

Sto facendo un piccolo programma, che ha pochi thread, costantemente in esecuzione. Ad un certo punto, potrei voler fermare uno di essi, e poi, dopo un periodo di tempo casuale, ricominciare da capo. Quindi, per prima cosa, qual è il modo migliore per definire un Thread, ecco il mio codice

private void startCheckCommandThread() {
        isRunning = true;
        //commandThread = new Thread(checkForCommand);
        //commandThread.IsBackground = true;
        //commandThread.Start();

        new Thread(() => {
            Thread.CurrentThread.IsBackground = true;
            checkForCommand();
        }).Start();
    }

Il modo commentato o l'altro? Ed ecco come sto fermandomi e riavviandoli:

// get condition variable
if (condition) {
    isRunning = false;
    // here it must be 100% guaranteed that the Thread will stop
} else {
    startCheckCommandThread();
}

Quindi qual è il modo migliore per farlo, principalmente mi chiedo della stabilità (che significa a prova di errore) e delle massime prestazioni (il minor utilizzo possibile della CPU e una minore allocazione di memoria possibile)?

    
posta Skaidar 01.04.2015 - 17:36
fonte

3 risposte

29

I am making a little program, which have few Threads, constantly running. At some point, I may want to stop one of them, and then, after random period of time, to start it again. What is the best way to create a thread?

Il modo migliore è non farlo affatto. Se ho un lavoro che deve essere fatto in parallelo con un altro programma, la migliore pratica è quella di avviare un altro processo . Se ho un lavoro che deve essere fatto in modo asincrono, allora uso la Task Parallel Library e lascia che gestisca i miei thread.

I thread sono semplicemente l'unità sbagliata di granularità per la maggior parte delle attività. I thread sono worker ; invece di gestire worker , gestisci invece tasks ; lascia che il TPL capisca quanti lavoratori sono necessari.

mostly I am asking about stability (meaning error-proof)

Il threading code è notoriamente difficile da correggere. Devi preoccuparti della coerenza, delle barriere della memoria, delle letture e scritture non atomiche, dei deadlock, dei livelock, ... Ecco perché usare i processi è di gran lunga migliore.

Se devi tenere tutto in un unico processo, usa lo strumento livello più alto disponibile. Ancora una volta, non tentare di manipolare direttamente i thread. Se hai un lavoro asincrono da fare, usa il TPL. Se hai bisogno di cancellare parte di quel lavoro, usa i token di cancellazione. Non tentare di controllare direttamente le discussioni; ti sbaglierai.

Se per qualche motivo non si desidera utilizzare la TPL e si desidera utilizzare direttamente i thread, quindi creare uno schema in cui i thread comunicano attraverso canali ben definiti e testati e far revisionare il codice da esperti. Non si vuole mai interrompere un thread se non in condizioni di emergenza; così facendo puoi corrompere le tue strutture dati in modi insoliti. Se hai bisogno di chiudere un thread, allora devi avere un sistema attentamente progettato e implementato con cura che permetta a un thread di segnalare chiaramente un altro thread che è ora di spegnersi e che il thread si chiuda in modo pulito.

and maximum performance (as low CPU usage as possible, and as less memory allocation as possible)?

Non capisco mai perché la gente dice "Voglio usare i thread per mantenere basso l'utilizzo della mia CPU". I thread sono lì per mantenere l'utilizzo della CPU alto . Hai pagato un buon prezzo per quelle CPU; usali! La situazione ideale è avere tutte le CPU della macchina che vanno sempre al 100% facendo un lavoro utile. Non diresti che il tuo obiettivo era costruire una fabbrica dove i lavoratori erano inattivi il 95% delle volte, quindi perché dovresti fare lo stesso per un programma?

I thread mantengono alto l'utilizzo della CPU perché, se sei intelligente, assegnerai un thread di lavoro con CPU a ogni CPU, e avrai quella nient'altro diversa dal lavoro sul problema che ci provi. Se hai un lavoro più legato alla CPU rispetto alle CPU e più thread per gestirlo, le CPU passeranno avanti e indietro tra i thread e tutto diventerà meno efficiente.

Se si desidera evitare l'allocazione di memoria, mantenere sempre il numero di thread il più basso possibile. I thread riguardano la maggior parte delle risorse costose di memoria che è possibile allocare. Ciascuno di default consuma un milione di byte di spazio indirizzo per lo stack.

    
risposta data 01.04.2015 - 18:13
fonte
4

Per quanto riguarda i due modi di iniziare il thread, sono approssimativamente equivalenti, quindi è una questione di se preferisci un lambda.

Per fermare il thread, il modo più a prova di errore è controllare un valore di campo per una modifica. Questo può essere fatto usando una barriera di memoria, per esempio, usando un'istruzione di blocco o un monitor fornirà automaticamente una barriera di memoria. Il modo più semplice per sincronizzare i thread consiste nell'utilizzare un'istruzione di blocco attorno a tutti gli accessi e le modifiche di un campo booleano. Il campo sarebbe contenuto in un oggetto associato al thread da arrestare.

Quando la variabile cambia, si esce dal thread ritornando dal metodo di livello superiore che si sta eseguendo nel thread.

La maggior parte degli sviluppatori non "riusano" un thread. Una volta uscito, non c'è più: devi crearne uno nuovo. È possibile riutilizzare l'oggetto associato al thread. Mentre si può tenere il filo sospeso, sospenderlo fino a quando non ne hai bisogno, generalmente questo è considerato di scarsa progettazione, poiché il filo spreca risorse mentre è sospeso. Inoltre, deve bloccare in modo tale da non bruciare cicli durante l'attesa.

Se ti interessa il massimo delle prestazioni, potresti voler controllare la sincronizzazione usando Interlocked. Questo è più veloce, ma difficile da usare. Non usarlo se stai iniziando con il multithreading. Raccomando questo sito Web per esercitazioni di threading: Threading in C #

    
risposta data 01.04.2015 - 18:01
fonte
2

Il modo migliore per gestire i thread è non farlo.

L'uso del threading non è un obiettivo, è un mezzo per un fine, vale a dire. concorrenza . In C # otteniamo la concorrenza senza la gestione diretta dei thread in virtù delle espressioni lambda / await, o qualche volta (come detto da altri) lambda, più comunemente tramite PLINQ.

È costoso, lento e soggetto a disponibilità per avviare e interrompere i thread. Invece, gli approcci di cui sopra utilizzano un pool di thread preallocati. Le risorse vengono organizzate al momento dell'impostazione e della demolizione del processo tramite codice scritto da persone con esperienza significativa nella gestione dei thread.

Hanno anche accesso agli sviluppatori della piattaforma e ai team di test su larga scala che hanno anche esperienza adeguata. È improbabile che qualsiasi team di sviluppo dell'applicazione possa eguagliarlo.

    
risposta data 02.04.2015 - 00:07
fonte

Leggi altre domande sui tag