2012-03-12 15 views
12

Nel mio progetto attuale c'è una classe Form che assomiglia a questo:Problemi di risoluzione "Impossibile accedere all'oggetto disposto". eccezione

public partial class FormMain : Form 
{ 

    System.Timers.Timer timer; 
    Point previousLocation; 
    double distance; 

    public FormMain() 
    { 
     InitializeComponent(); 

     distance = 0; 
     timer = new System.Timers.Timer(50); 
     timer.AutoReset = true; 
     timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed); 
     timer.Start(); 
    } 

    private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 
    { 
     if (previousLocation != null) 
     { 
      // some code 

      UpdateDistanceLabel(distance); 
      UpdateSpeedLabel(v); 
     } 

     previousLocation = Cursor.Position; 
    } 

    private void UpdateDistanceLabel(double newDistance) 
    { 
     if (!lblDistance.IsDisposed && !IsDisposed) 
     { 
      Invoke(new Action(() => lblDistance.Text = String.Format("Distance: {0} pixels", newDistance))); 
     } 
    } 

    private void UpdateSpeedLabel(double newSpeed) 
    { 
     if (!lblSpeed.IsDisposed && !IsDisposed) 
     { 
      Invoke(new Action(() => lblSpeed.Text = String.Format("Speed: {0} pixels per second", newSpeed))); 
     } 
    } 

} 

Come potete vedere, sto usando un oggetto System.Timers.Timer. So che potrei usare System.Windows.Forms.Timer, ma sono abbastanza interessato al motivo per cui sto ancora ricevendo l'eccezione mostrata nel titolo. Viene generato alla chiamata Invoke nel metodo UpdateDistanceLabel. Ciò che mi confonde è che dice "Impossibile accedere all'oggetto disposto: FormMain" anche se sto verificando se è disposto o meno. In modo che non dovrebbe accadere. Ho anche provato a smaltire l'oggetto timer nell'evento FormClosing, oltre a ignorare Dispose (bool) ea smaltirlo lì, che purtroppo non sono stati di aiuto. Inoltre, l'eccezione non viene sempre generata, presumibilmente solo quando il timer si accende mentre il programma sta uscendo. Succede ancora molto.

Ho visto che ci sono un sacco di discussioni su questo, ma ho già provato le soluzioni pubblicate lì, la maggior parte di esse riguarda il controllo della proprietà IsDisposed - che non funziona per me. Quindi immagino di fare qualcosa di sbagliato.

Quindi la mia domanda: Perché il codice pubblicato sopra attiva un'eccezione anche se sto verificando se gli oggetti che sto accedendo sono disposti o meno?

risposta

9

Esistono due soluzioni: o ingoiare l'eccezione e la maledizione di Microsoft per non aver incluso un TryInvoke e TryBeginInvoke metodi, o l'uso altro blocco per garantire che nessun tentativo di Dispose l'oggetto mentre è in uso, e nessun tentativo è fatto per utilizzare l'oggetto mentre Dispose è in corso. Penso che la deglutizione dell'eccezione sia probabilmente migliore, ma alcune persone hanno una reazione viscerale nei confronti di queste cose, e usando il blocco è possibile evitare di fare in modo che l'eccezione si verifichi in primo luogo.

9

Un problema è che si sta eseguendo il controllo sul thread del timer, prima di chiamare Invoke. Esiste una possibile condizione di competizione, in cui il modulo può essere eliminato dopo il controllo e prima che l'azione invocata venga eseguita.

Si dovrebbe eseguire il controllo all'interno del metodo (espressione lambda nel proprio caso) chiamato da Invoke.

Un altro possibile problema è che si sta accedendo a Cursor.Position sul thread del timer. Non sono sicuro che sia valido - lo farei sul thread principale. Il tuo codice include anche il commento //some code - quindi hai presumibilmente omesso del codice che devi anche controllare.

Nel complesso, probabilmente starai meglio con uno System.Windows.Forms.Timer.

+0

Grazie. Questo è quello che ho fatto ora, tuttavia, non ha influenzato molto il comportamento. – haiyyu

6

Qui è la mia soluzione al vostro un'eccezione, se siete interessati:

private void FormMain_FormClosing(object sender, FormClosingEventArgs e) 
     { 
      timer.Stop(); 
      Application.DoEvents();  
     } 

.Stop() senza .DoEvents() non è sufficiente, in quanto sarà smaltire gli oggetti senza aspettare il vostro filo per terminare la sua lavoro.

+0

Sì, veloce e sporco ma farà il trucco. E mostra la causa principale. –

+0

Chiamare 'Application.DoEvents' non garantirà di evitare la potenziale condizione di competizione, anche se probabilmente lo renderà meno probabile. – Joe

+0

Può anche aggiungere Thread.Sleep (100); dopo .DoEvents(), tuttavia, è ancora più sporco:/ –

0

Creare due booleani chiamati "StopTimer" e "TimerStopped" con i loro stati iniziali impostati su falso. Imposta la proprietà AutoReset del timer su false. Quindi formattare il metodo trascorso al seguente:

Invoke((MethodInvoker)delegate { 
    // Work to do here. 
}); 
if (!StopTimer) 
    timer.Start(); 
else 
    TimerStopped = true; 

In questo modo si impedisce una condizione di competizione, controllando se il timer deve continuare e reporting quando il metodo ha raggiunto la fine.

Ora impostare il metodo di FormClosing a questo:

if (!TimerStopped) 
{ 
    StopTimer = true; 
    Thread waiter = new Thread(new ThreadStart(delegate { 
     while (!TimerStopped) { } 
     Invoke((MethodInvoker)delegate { Close(); }); 
    })); 
    waiter.Start(); 
    e.Cancel = true; 
} 
else 
    timer.Dispose(); 

Se il timer non si è ancora fermato, un filo viene lanciato ad aspettare fino a quando non ha fatto così e quindi provare a chiudere nuovamente il modulo.

Problemi correlati