2012-01-04 16 views
27

Ho un server che gestisce più connessioni socket in entrata e crea 2 thread diversi che memorizzano i dati in formato XML.Quando utilizzare il thread di blocco in C#?

Stavo usando l'istruzione lock per la sicurezza del thread quasi in ogni gestore di eventi chiamato asincronicamente e nei 2 thread in diverse parti del codice. Usando tristemente questo approccio la mia applicazione rallenta notevolmente.

Ho provato a non utilizzare lock e il server è molto veloce in esecuzione, anche l'archiviazione dei file sembra aumentare; ma il programma si blocca per motivi che non capisco dopo 30 secondi - 1 minuto. di lavoro.

Così. Pensavo che il modo migliore fosse usare meno lucchetti o usarlo solo là dove è strettamente necessario. Come tale, ho 2 domande:

  1. è la serratura necessaria quando scrivo per le variabili pubbliche accessibili (C# liste) solo o anche quando ho letto da loro?

  2. Il blocco è necessario solo nei thread asincroni creati dal gestore socket o anche in altri luoghi?

Qualcuno potrebbe darmi alcune linee guida pratiche, su come operare. Non posterò l'intero codice questa volta. Non ha senso pubblicare circa 2500 righe di codice.

+2

Non abbastanza specifico, guarda qui per informazioni generali: http://www.albahari.com/threading/ – eulerfx

+1

C'è tutta una serie di possibili programmi - da assolutamente sbagliato/incredibilmente veloce a sempre corretto/ragionevolmente lento. Sembri colpire entrambe le estremità nei tuoi esperimenti. La sicurezza del threading non è un argomento banale, quindi consiglierei di leggerlo piuttosto che cercare di ottenere "linee guida pratiche". Cioè Il link di eulerfx è un buon punto di partenza. –

risposta

33

Il blocco è necessario quando scrivo solo alle variabili accessibili al pubblico (elenchi C#) o anche quando leggo da esse?

Sì (anche quando si legge).

Il blocco è necessario solo nei thread asincroni creati dal gestore socket o anche in altri luoghi?

Sì. Ovunque il codice acceda a una sezione di codice condivisa, blocca sempre.


Questo suona come potrebbe non essere fissando la singoli oggetti, ma bloccando una cosa per tutti le situazioni di blocco.

caso affermativo batte intelligenti discrete serrature creando singoli oggetti unici che riferiscono e bloccare solo determinate sezioni alla volta, che non interferiscono con altri fili in altre sezioni.

Ecco un esempio:

// This class simulates the use of two different thread safe resources and how to lock them 
// for thread safety but not block other threads getting different resources. 
public class SmartLocking 
{ 
    private string StrResource1 { get; set; } 
    private string StrResource2 { get; set; } 

    private object _Lock1 = new object(); 
    private object _Lock2 = new object(); 

    public void DoWorkOn1(string change) 
    { 
     lock (_Lock1) 
     { 
      _Resource1 = change; 
     } 
    } 

    public void DoWorkOn2(string change2) 
    { 
     lock (_Lock2) 
     { 
      _Resource2 = change2; 
     } 
    } 
} 
+3

Ehi ragazzo ... mi hai salvato la vita ..Ho usato diversi lucchetti (con diversi oggetti statici) per diverse situazioni e ora l'app funziona come un incantesimo. Grazie mille. –

+0

Sono sorpreso che questo thread sia bloccato! Nessun gioco di parole previsto. L'uso improprio di serrature può portare a condizioni di gara defacto che mi è sembrato essere il caso. Sono felice che la mia risposta sia stata in grado di aiutarti. – OmegaMan

+10

@ClaudioFerraro: Va bene, ma ora puoi scambiare un problema (cattiva prestazione) con un altro (deadlock). È possibile entrare in una situazione in cui il thread 1 ha eliminato il blocco A e sta aspettando il blocco B, e il thread 2 ha eliminato il blocco B e sta aspettando il blocco A, e quindi ovviamente entrambi aspetteranno per sempre. Come si aggiunge un blocco a grana fine al proprio programma ** è necessario stabilire un rigido protocollo di ordinazione per tutte le serrature **. Devi dire, ad esempio "Non permetterò mai a nessun thread di chiedere il blocco 1 * dopo che * ha già ottenuto il blocco 2". I tuoi problemi stanno appena iniziando; questo è difficile. –

0

Fondamentalmente questo può essere risolta piuttosto semplice:

È necessario bloccare tutte le cose a cui si accede da diversi thread. In realtà non importa se si tratta di leggere o scrivere. Se stai leggendo e un altro thread sta sovrascrivendo i dati nello stesso momento, i dati letti potrebbero non essere più validi e probabilmente stai eseguendo operazioni non valide.

2

Utilizzare sempre un blocco quando si accede ai membri (sia in lettura che in scrittura). Se stai iterando su una raccolta e da un'altra discussione stai rimuovendo gli elementi, le cose potrebbero andare male rapidamente.

Un suggerimento è quando si desidera ripetere una raccolta, copiare tutti gli elementi in una nuova raccolta e quindi iterare la copia. Cioè

var newcollection; // Initialize etc. 
lock(mycollection) 
{ 
    // Copy from mycollection to newcollection 
} 

foreach(var item in newcollection) 
{ 
    // Do stuff 
} 

Allo stesso modo, utilizzare la serratura solo nel momento in cui si sta effettivamente scrivendo alla lista.

85

Sei mai seduto in auto o sull'autobus a un semaforo rosso quando non c'è traffico? Grande spreco di tempo, giusto? Una serratura è come un semaforo perfetto. È sempre verde tranne quando c'è traffico nell'intersezione.

La tua domanda è "Passo troppo tempo nel traffico di attesa al semaforo rosso. Dovrei semplicemente eseguire la luce rossa? O meglio ancora, dovrei togliere le luci del tutto e lasciare che tutti guidare attraverso l'incrocio a velocità autostradale senza qualsiasi controllo di intersezione? "

Se si verifica un problema di prestazioni con i blocchi, rimuovere i blocchi è lo ultima cosa che si dovrebbe fare ultimo. Stai aspettando quella luce rossa proprio perché c'è un incrocio nell'intersezione. Le serrature sono straordinariamente veloci se non sono contese.

Non è possibile eliminare la luce senza eliminare prima il traffico incrociato. La soluzione migliore è quindi eliminare il traffico incrociato. Se il lucchetto non è mai conteso, non lo aspetteresti mai. Capire perché il traffico trasversale sta trascorrendo così tanto tempo nell'incrocio; non rimuovere la luce e spero che non ci siano collisioni. Ci sarà.

Se non si riesce a farlo, il aggiunge più lucchetti a grana fine a volte aiuta. Cioè, forse hai ogni strada in città che converge sullo stesso incrocio. Forse puoi dividerlo in due incroci, in modo che il codice possa spostarsi attraverso due diversi incroci allo stesso tempo.

Nota che rendere le vetture più veloci (ottenendo un processore più veloce) o rendendo le strade più brevi (eliminando codice di lunghezza del percorso), spesso rende il problema peggiore in scenari multithread. Proprio come fa nella vita reale; se il problema è l'ingorgo, acquistare auto più veloci e guidarle su strade più brevi li fa più velocemente all'ingorgo, ma non più velocemente.

+22

+1, mi piacciono sempre queste risposte di tipo metaforico. Rende le cose molto più facili da capire. – jadarnel27

+1

Grazie per la risposta. La tua risposta è stata piuttosto chiara, ma il mio intento era quello di chiedere: "se nella vita reale le persone hanno inventato le cerchie del traffico, come posso determinare quanto dovrebbero essere grandi per avere prestazioni migliori." Mi aspettavo solo una risposta più specifica. dovrebbe essere circa 100 volte più grande dell'anello di mia moglie "o qualcosa del genere! –

1

La ragione per cui è necessario bloccare, mentre la lettura è:

diciamo che si stanno facendo modifica a una proprietà, che v'è la lettura due volte mentre il filo è inbetween una serratura. Una volta prima di fare qualsiasi modifica e un'altra dopo, avremo risultati incoerenti.

Mi auguro che aiuta,

Problemi correlati