2009-06-28 15 views
5

Ho un'applicazione con due thread.Chiude con grazia un'applicazione multithreading?

Il primo (il filo principale) che acquisisce i dati utilizzando presa e aggiornare DataTables

I secondi inserti i DataTables nel database.

L'applicazione funziona correttamente ma, una volta chiusa, il thread principale termina la lettura dei dati e chiama il metodo Abort nel secondo thread, che potrebbe essere inserito nel database e ciò porta a dati incoerenti.

Attualmente sto usando la seguente soluzione per superare "interruzione durante l'inserimento"

EDIT: Dopo le risposte potenti ho cambiato il codice

void MainThread() 
{ 
    while(Read()) 
    { 
     //Read Data through socket 
     try 
     { 
      //Wait on Mutex1 
      //Update Tables 
     } 
     finally 
     { 
      //Release Mutex1 
     } 
    } 
    _isrunning = false; 
    _secondThread.Join(); 
} 
void SecondThread() 
{ 
    while(_isrunning) 
    { 
     try 
     { 
      //Wait on Mutex1 
      //Insert Tables into Database using transactions 
     } 
     finally 
     { 
      //Release Mutex1   
     } 
    } 
} 
+5

Quindi .. non chiamare "Abort"! Non è una buona idea. Mai. –

+1

Re il tuo commento "Non voglio usare la transazione (per le prestazioni), quindi senza usare mutex e thread.l'applicazione interromperà a record incoerenti "- Devo passare a Sam, che è semplicemente folle.La maggior parte dei database sono completamente ottimizzati per le transazioni (infatti, spesso solo un rollback comporta costi aggiuntivi). Non importa quanto sia veloce la tua app può funzionare se corrompe lo stato, farlo funzionare prima in modo robusto, quindi (se è troppo lento) profilarlo e trovare i colli di bottiglia delle prestazioni effettivi e correggerli. Non danneggiare il database ... –

+0

In particolare, il tuo mutex funziona * * niente ** per aiutare quando il tuo exe muore a causa di fattori esterni ("processo di uccisione", BSOD, o (più probabilmente) un'interruzione dell'alimentazione) .Perché una tua operazione è progettata per rendere sicuro il tuo inserto, perché non ti piacciono ? Inserimento –

risposta

6

Supponendo "metodo interruzione chiamata" indica l'interruzione del filo utilizzando Thread.Abort. Don't do that.

Si sta effettivamente bloccando la tua app. Ci sono molti modi più puliti per farlo con i monitor.

Tuttavia, non si dovrebbero ottenere dati incoerenti nel DB quando l'app si arresta in modo anomalo, ecco perché si dispone di transazioni DB con le proprietà ACID.

MODIFICA MOLTO IMPORTANTE Ha detto: non si utilizzano le transazioni per motivi di prestazioni e si utilizzano invece i mutex. Questo è SBAGLIATO su alcuni livelli. In primo luogo, le transazioni possono rendere più veloci alcune operazioni, ad esempio provare a inserire 10 righe in una tabella, riprovare in una transazione, la versione della transazione sarà più veloce. In secondo luogo, cosa succede quando/se la tua app si arresta in modo anomalo, danneggia il tuo DB? Cosa succede quando sono in esecuzione più istanze della tua app? O mentre esegui rapporti sul tuo DB in Query Analyzer?

+0

Non voglio utilizzare la transazione (per prestazioni ragionevoli), quindi senza utilizzare mutex e thread.abort l'applicazione porterà a record incoerenti –

+1

Dopo il commento e la vostra modifica, vorrei poterlo revocare nuovamente una seconda volta. –

+0

Ho solo una singola istanza della mia applicazione e non uso la transazione durante l'inserimento poiché il metodo di inserimento viene eseguito ogni secondo e non dovrebbe richiedere più della seconda risposta –

8

Finché entrambi i fili non sono contrassegnati come sfondo discussioni, l'app continuerà a essere eseguita fino all'uscita di entrambi i thread. Quindi, in realtà, tutto ciò che devi fare è ottenere ogni thread separatamente per uscire in modo pulito. Nel caso del thread che scrive sul database, questo potrebbe significare esaurire una coda produttore/consumatore e controllare un flag per uscire.

ho mostrato un adeguato coda di produttore/consumatore here - il lavoratore sarebbe solo:

void WriterLoop() { 
    SomeWorkItem item; // could be a `DataTable` or similar 
    while(queue.TryDequeue(out item)) { 
     // process item 
    } 
    // queue is empty and has been closed; all done, so exit... 
} 

Ecco un esempio completo in base a SizeQueue<> - Si noti che il processo non esce fino a quando il lettore e lo scrittore è uscito pulito. Se non vuoi scaricare la coda (cioè, vuoi uscire prima e dimenticare qualsiasi lavoro in sospeso), allora bene - aggiungi un altro flag (volatile) da qualche parte.

static class Program { 
    static void Write(object message) { 
     Console.WriteLine(Thread.CurrentThread.Name + ": " + message); 
    } 
    static void Main() { 
     Thread.CurrentThread.Name = "Reader"; 
     Thread writer = new Thread(WriterLoop); 
     writer.Name = "Writer"; 
     var queue = new SizeQueue<int>(100); 
     writer.Start(queue); 
     // reader loop - note this can run parallel 
     // to the writer 
     for (int i = 0; i < 100; i++) { 
      if (i % 10 == 9) Write(i); 
      queue.Enqueue(i); 
      Thread.Sleep(5); // pretend it takes time 
     } 
     queue.Close(); 
     Write("exiting"); 
    } 
    static void WriterLoop(object state) { 
     var queue = (SizeQueue<int>)state; 
     int i; 
     while (queue.TryDequeue(out i)) { 
      if(i%10==9) Write(i); 
      Thread.Sleep(10); // pretend it takes time 
     } 
     Write("exiting"); 
    } 
} 
+0

Ancora un arresto anomalo dell'app non dovrebbe causare il danneggiamento del db ... –

+0

Vero, gli aggiornamenti individuali dovrebbero essere ACID - ma il crash qui è autoinflitto chiamando Abort. –

+0

c'è un caso limite in cui è possibile che si verifichi un arresto anomalo, ad es. in un servizio di Windows hai uno SLA che vuoi incontrare quando chiudi un'app. Se per qualche motivo una chiamata è "bloccata" al db a causa del blocco e non puoi segnalarlo, potresti prendere in considerazione l'annullamento di –

3

L'attesa del mutex dovrebbe comportare un timeout. Il ciclo esterno di ogni thread può verificare la presenza di un flag "please close now". Per chiudere, imposta il flag "please close now" per ogni thread, quindi usa "join" per attendere il completamento di ogni thread.

+0

meravigliosa, semplice ed efficace –