2010-02-16 12 views
5

Sto facendo una patch per risolvere un problema della barra di avanzamento in un'applicazione che è un po 'incasinato. L'annullamento sulla barra di avanzamento utilizzato per fare un Thread.Abort sul thread facendo il lavoro pesante. L'ho modificato per innalzare un flag di annullamento che posso verificare in un punto strategico nel thread.Come evitare di morire di fame il filo principale?

La maggior parte delle volte funziona, ma una volta tanto la cancellazione non funziona affatto. Suppongo di poter fare uno Application.DoEvents prima di guardare lo stato della bandiera (non vi è alcun rischio di rientro), ma vorrei un'opzione più "pulita".

Apprezzerei se qualcuno potesse fornirmi informazioni per capire cosa sta succedendo esattamente e come questa roba funziona dietro la scena. Mi piacerebbe sapere come affrontare questo problema senza usare lo BackgroundWorker (come si farebbe con .net 1.1) ma vorrei anche sapere se lo BackgroundWorker risolve questo tipo di problemi e come lo fa.

Modifica: Sto prendendo nota dei tuoi suggerimenti e proverò domani e riferirò. All'inizio ho usato un bool volatile, penso di averlo aggiornato ad una proprietà automatica e ho dimenticato la volatilità. È possibile che il thread di lavoro continui a cercare il valore memorizzato nella cache ancora e ancora? Non vedo come potrei avere un punto morto. Il lavoratore per controllare la bandiera da quando sono riuscito a rompere lì inserendo un punto di interruzione al volo. Io cerco sempre lo stesso insieme di dati e il più delle volte cancella perfettamente. L'unica cosa che cambia tra i test è il momento in cui premo Annulla. Finora, ho testato solo in debug, avviato da VS.

Modifica 2: il mio problema non è correlato alla mia bandiera o a qualcosa che ho aggiunto. È più un problema di WinForm. Il programma arriva a chiamare uno ShowDialog (e c'è già un altro ShowDialog bloccato). Non riesco a trascinare il modulo e non si aggiorna da solo. Il pulsante Annulla non funziona nemmeno. Ecco lo stack delle chiamate quando metto in pausa tutto.

 
[Code externe] 
    Mrnf.Son.Commun.dll!Mrnf.Son.Commun.Messages.BarreProgressionBase.ShowDialog(System.Windows.Forms.IWin32Window fenetre = {Mrnf.Son.Presentation.Windows.UI.Echanges.AssistantForm}) Ligne 274 + 0xb octets C# 
    Mrnf.Son.Commun.dll!Mrnf.Son.Commun.Controleurs.Utils.AttendreFinTraitement(System.Windows.Forms.Form parent = {Mrnf.Son.Presentation.Windows.UI.Echanges.AssistantForm}, Mrnf.Son.Commun.Messages.BarreProgressionBase progressionBase = {Mrnf.Son.Commun.Messages.BarreProgressionMessage}, System.Threading.Thread thread = {System.Threading.Thread}) Ligne 302 + 0xd octets C# 
    Mrnf.Son.Affaires.dll!Mrnf.Son.Affaires.Persisteurs.Echanges.LecteurDBFGeneriqueCollection.Importer(System.Windows.Forms.Form parent = {Mrnf.Son.Presentation.Windows.UI.Echanges.AssistantForm}) Ligne 95 + 0x1d octets C# 
    Mrnf.Son.Affaires.dll!Mrnf.Son.Affaires.Persisteurs.Echanges.PersisteurModeleEchanges.Importer(Mrnf.Son.Affaires.Entites.Echanges.ModeleEchanges unModele = {Mrnf.Son.Presentation.Windows.Controleurs.Echanges.ModeleEchanges.ModeleEchangesGenerique}, System.Windows.Forms.Form formParent = {Mrnf.Son.Presentation.Windows.UI.Echanges.AssistantForm}) Ligne 1880 + 0xd octets C# 
    Mrnf.Son.Affaires.dll!Mrnf.Son.Affaires.Entites.Echanges.ModeleEchanges.Importer(System.Windows.Forms.Form formParent = {Mrnf.Son.Presentation.Windows.UI.Echanges.AssistantForm}) Ligne 875 + 0x18 octets C# 
    Mrnf.Son.Presentation.Windows.exe!Mrnf.Son.Presentation.Windows.UI.Echanges.AssistantForm.EffectuerImport(Mrnf.Son.Affaires.Entites.Echanges.IModeleEchanges modele = {Mrnf.Son.Presentation.Windows.Controleurs.Echanges.ModeleEchanges.ModeleEchangesGenerique}) Ligne 1429 + 0xc octets C# 
    Mrnf.Son.Presentation.Windows.exe!Mrnf.Son.Presentation.Windows.UI.Echanges.AssistantForm._terminerBtn_Click(object sender = {Text = Impossible d'évaluer l'expression, car un frame natif se trouve en haut de la pile des appels.}, System.EventArgs e = {System.EventArgs}) Ligne 1334 + 0x1d octets C# 
[Code externe] 
    Mrnf.Son.Presentation.Windows.exe!Mrnf.Son.Presentation.Windows.UI.Echanges.AssistantForm.WndProc(ref System.Windows.Forms.Message m = {System.Windows.Forms.Message}) Ligne 1133 + 0xb octets C# 
[Code externe] 
    Mrnf.Son.Presentation.Windows.exe!Mrnf.Son.Presentation.Windows.Controleurs.Sondages.ActionsSondages.OnImporterSysExt() Ligne 1362 + 0x1f octets C# 
    Mrnf.Son.Presentation.Windows.exe!Mrnf.Son.Presentation.Windows.UI.Sondages.UEExploitationVue._mniImporterSysExt_Click(object sender = {System.Windows.Forms.ToolStripMenuItem}, System.EventArgs e = {System.EventArgs}) Ligne 820 + 0x12 octets C# 
[Code externe] 
    Mrnf.Son.Presentation.Windows.exe!Mrnf.Son.Presentation.Windows.Program.Main() Ligne 148 + 0x8 octets C# 
[Code externe] 

Edit 3: Se passo null al ShowDialog funziona benissimo (l'interfaccia utente non gela, il funzionamento del pulsante cancella, cancella bene). Non capisco davvero la magia dietro a tutto questo.

risposta

1

Gli altri post sono probabilmente in linea con il perché.

Mi piace usare WaitHandles in combinazione con Thread.Join()/Thread.Abort() quando sto tentando di eliminare un thread.

private readonly ManualResetEvent _ExitThreadsEvent = new ManualResetEvent(false); 
private Thread _MyThread; 

public void Stop() 
{ 
    _ExitThreadsEvent.Set(); 

    if (_MyThread != null) 
    { 
     if (!_MyThread.Join(5000)) 
     { 
      _MyThread.Abort(); 
     } 

     _MyThread = null; 
    } 
} 

private void MyThread() 
{ 
    if (!_ExitThreadsEvent.WaitOne(1)) 
    { 
     // Do some work... 
    } 

    if (!_ExitThreadsEvent.WaitOne(1)) 
    { 
     // Do some more work... 
    } 
} 

Probabilmente è buono anche per capire il problema del deadlock originale.

+0

C'è qualche vantaggio nell'usare 'ManuelResetEvent' su un flag' volatile' in quella situazione? Oltre alla possibilità di bloccare se necessario (cosa che non faccio), non ne vedo. –

+0

Lo uso perché è progettato specificamente per segnalare uno o più thread e non richiede molta attenzione per implementare correttamente. Nel complesso è probabilmente "più pesante" di una bandiera volatile. Entrambi funzioneranno, preferisco uno rispetto all'altro :-) –

+0

Ok. Il mio problema è chiaramente un deadlock non correlato alla mia modifica. I deadlock quando si chiama 'ShowDialog' su un clic del pulsante già in una finestra di dialogo. Presumo che la chiamata multipla a ShowDialog sia negativa. Modifica: prevediamo di bloccare quelli ShowDialog ma siamo comunque in grado di elaborare eventi come il pulsante Annulla su di esso. –

4

BackgroundWorker non fa nulla in più qui, tranne che per fornire quella bandiera in una posizione facile da controllare. Quindi ci sono un paio di possibilità:

  • il codice è di arrivare a controllare il flag, ma non notando che ha cambiato (teoricamente possibile se la bandiera non è sincronizzato o volatili, ma molto improbabile nel codice non banale)
  • il codice non è di arrivare a controllare il flag

si suppone quest'ultimo; alcune cause comuni di che:

  • state accidentalmente deadlocking te stesso (forse cercando di Invoke indietro al thread dell'interfaccia utente che è già in attesa di un thread in background, o entrare in un loop attorno a un blocco o simili)
  • si sta chiamando fuori sopra COM (o simili), e quella chiamata è mai completando - uno scenario particolarmente difficile sfuggire, in alcuni casi

Puoi restringere i quali di questi è?Forse inietti del codice per tenere traccia di ciò che il tuo thread sta facendo a intervalli, e assicurati che stia facendo qualcosa di utile - o, in caso contrario, traccia dove è bloccato.

2

Application.DoEvents è un mezzo per consentire l'elaborazione degli eventi in sospeso nella pompa dei messaggi. Normalmente non dovrebbe assolutamente avere nulla a che fare con il thread in background.

Se l'annullamento "non funziona affatto", la soluzione dipenderà in gran parte da cosa significa "non funziona affatto". Non riesci a cambiare la bandiera? L'interfaccia utente è bloccata? Il thread in background non risponde al flag chane? È qualcos'altro? La soluzione dipende principalmente da quale sia esattamente il problema. Potrebbe essere che non stai controllando la bandiera dallo sfondo, potrebbe essere che blocchi i due thread. Mostrare il codice o elaborare i dettagli del problema potrebbe aiutare.

2

Questo è quasi sempre facile eseguire il debug. Quando vedi il thread di lavoro che ignora la richiesta di cancellazione, usa Debug + Break All. Quindi fai il debug + Windows + Threads e fai doppio clic sul thread worker. Quindi guarda lo stack di chiamate per vedere cosa sta facendo il thread e perché non passa attraverso il codice che controlla il flag.

Attenzione che è necessario dichiarare il membro di bandiera con la parola chiave volatile. Ciò impedisce al compilatore JIT di generare codice macchina che carica il valore membro in un registro e non verifica mai il valore della variabile effettiva in memoria. Che è probabile che si verifichi quando si esegue la versione di rilascio del programma senza un debugger. In tal caso, assicurarsi di utilizzare Strumenti + Allega a processo per collegare il debugger prima di utilizzare il comando Interrompi tutto.

A ManualResetEvent, controllato con una chiamata WaitOne (0) è migliore.

Problemi correlati