2012-05-06 23 views
12

Come posso mettere in pausa/riprendere una discussione? Una volta che ho Join() un thread, non riesco a riavviarlo. Quindi, come posso avviare un thread e metterlo in pausa ogni volta che viene premuto il pulsante "pause" e riprenderlo quando viene premuto il pulsante Riprendi?come mettere in pausa/riprendere una discussione

L'unica cosa che questo thread fa è mostrare del testo casuale in un controllo etichetta.

+1

Puoi aggiungere il tuo codice nella tua domanda – Adil

+0

Al momento non vi è alcun codice. Sto ancora pensando a come implementare questo progetto che ho nella mia testa. Tutto quello che posso dirti che so per esperienza con il mio lavoro con il threading, una volta che un thread è unito al thread principale, non c'è modo che io possa riprenderlo/riavviarlo. – Yustme

risposta

16

Forse il ManualResetEvent è una buona scelta. Un breve esempio:

private static EventWaitHandle waitHandle = new ManualResetEvent(initialState: true); 

// Main thread 
public void OnPauseClick(...) { 
    waitHandle.Reset(); 
} 

public void OnResumeClick(...) { 
    waitHandle.Set(); 
} 

// Worker thread 
public void DoSth() { 
    while (true) { 
    // show some random text in a label control (btw. you have to 
    // dispatch the action onto the main thread) 
    waitHandle.WaitOne(); // waits for the signal to be set 
    } 
} 
+0

Non funziona in VirtualBox XP. Non so perché? – zionpi

+0

Sono stato tentato di downvotare per l'utilizzo di [_erver_riservati_] (https://msdn.microsoft.com/en-us/library/x53a06bb.aspx) come nome di variabile, ma la soluzione ha funzionato perfettamente troppo bene per farmi troll :-) – MDMoore313

+0

Non sto definendo eventi (♡ Rx ♡), quindi non ero a conoscenza del fatto che 'event' è una parola chiave :-) –

5

potrei suggerire di leggere Threading in C#, by Joe Albahari, in particolare Suspend and Resume sezione:

Un thread può essere sospeso e ripreso esplicitamente tramite i metodi deprecati Thread.Suspend e Thread.Resume. Questo meccanismo è completamente separato da quello del blocco. Entrambi i sistemi sono indipendenti e funzionano in parallelo.

Un thread può sospendere se stesso o un altro thread. Chiamando Suspend i risultati nel thread entrano brevemente nello stato SuspendRequested, quindi al raggiungimento di un punto sicuro per la garbage collection, esso entra nello stato Sospeso. Da lì, può essere ripreso solo tramite un altro thread che chiama il suo metodo Resume. Resume funzionerà solo su un thread sospeso, non su un thread bloccato.

Da .NET 2.0, Sospensione e Riprendi sono stati deprecati, il loro utilizzo scoraggiato a causa del pericolo inerente alla sospensione arbitraria di un altro thread. Se un thread in possesso di un blocco su una risorsa critica è sospeso, l'intera applicazione (o computer) può deadlock. Questo è molto più pericoloso che chiamare Abort - il che si traduce in eventuali blocchi di questo genere rilasciati (almeno teoricamente) in virtù del codice in blocchi alla fine.

SuspendRequested state

+0

La citazione del blocco distingue tra un thread sospeso e un thread bloccato, ma il diagramma non sembra fare questa distinzione. È intenzionale? – Tung

+0

@gliderkite, vedere la mia risposta ad Adil sulla visualizzazione del codice. – Yustme

+0

@Tung Un thread viene considerato bloccato quando la sua esecuzione viene messa in pausa per qualche motivo, ad esempio quando si dorme o si attende che un altro termini con Join o EndInvoke. Nel codice: 'bool blocked = (someThread.ThreadState & ThreadState.WaitSleepJoin)! = 0;'. Quindi, perché non c'è alcuna distinzione nel diagramma. Vedi [this] (http://www.albahari.com/threading/part2.aspx#_Blocking). – gliderkite

2

Non è la migliore idea di sospendere e riprendere le discussioni manualmente. Tuttavia, è possibile simulare facilmente questo comportamento utilizzando le primitive di sincronizzazione dei thread (come ManualResetEvent)

Dai un'occhiata a this question, potresti trovarlo utile.

Ma credo che si possa raggiungere facilmente il proprio obiettivo di "mostrare testo casuale in un controllo etichetta" su base temporale utilizzando i timer.

Ecco un rapido esempio utilizzando DispatcherTimer

var timer = new DispatcherTimer(); 
timer.Tick += (s, e) => Label.Text = GetRandomText(); 
timer.Interval = TimeSpan.FromMilliseconds(500); 
timer.Start(); 

È possibile mettere in pausa chiamando timer.Stop() e poi timer.Start() per riprendere.

+0

Ciao, questo sembra carino, posso inserire questo codice anche in un thread separato ? Perché non voglio bloccare il thread della GUI. – Yustme

+0

@Yustme: AFAIK il codice di 'DispatcherTimer' viene eseguito sul thread della GUI. Se vuoi un thread separato usa 'System.Threading.Timer'. – Tudor

+0

Tudor ha ragione, quindi se hai qualche codice "pesante" che deve essere eseguito sui tick del timer, usa thread separato. Ma se hai solo bisogno di aggiornare il blocco di testo, usare 'DispatcherTimer' va bene e non bloccherà la tua interfaccia utente. –

1

Ecco due modi per cui ha funzionato. Entrambi presuppongono che il thread di lavoro abbia il proprio ciclo di elaborazione.

  1. avere la discussione invocare una richiamata per richiedere il permesso di andare avanti
  2. Avere il genitore invocare un metodo su di classe del thread per segnalarlo

L'esempio di applicazione di console in basso mostra entrambi gli approcci, utilizzando un callback per mettere in pausa/continuare e un metodo di lavoro per fermarsi.Un altro vantaggio del metodo di callback è che è anche conveniente per il passaggio degli aggiornamenti di stato mentre sta controllando il permesso di continuare.

using System; 
using System.Threading; 

namespace ConsoleApplication7 
{ 
    class Program 
    { 
     static bool keepGoing; 
     static void Main(string[] args) 
     { 
      keepGoing = true; 
      Worker worker = new Worker(new KeepGoingDelegate(KeepGoing)); 
      Thread thread = new Thread(worker.DoWork); 
      thread.IsBackground = true; 
      thread.Start(); 

      while (thread.ThreadState != ThreadState.Stopped) 
      { 
       switch (Console.ReadKey(true).KeyChar) 
       { 
        case 'p': 
         keepGoing = false; 
         break; 
        case 'w': 
         keepGoing = true; 
         break; 
        case 's': 
         worker.Stop(); 
         break; 
       } 
       Thread.Sleep(100); 
      } 
      Console.WriteLine("Done"); 
      Console.ReadKey(); 
     } 

     static bool KeepGoing() 
     { 
      return keepGoing; 
     } 
    } 

    public delegate bool KeepGoingDelegate(); 
    public class Worker 
    { 
     bool stop = false; 
     KeepGoingDelegate KeepGoingCallback; 
     public Worker(KeepGoingDelegate callbackArg) 
     { 
      KeepGoingCallback = callbackArg; 
     } 

     public void DoWork() 
     { 
      while (!stop) 
      { 
       Console.Write(KeepGoingCallback()?"\rWorking":"\rPaused "); 

       Thread.Sleep(100); 
      } 
      Console.WriteLine("\nStopped"); 
     } 

     public void Stop() 
     { 
      stop = true; 
     } 
    } 
} 
+0

Ciao, per la vita di me ho difficoltà a capire i delegati. Posso capire come fermare il thread, ma non capisco come lo stai riavviando? – Zev

+0

@Zev: un delegato è semplicemente un segnaposto per un metodo, in modo che sia possibile passarlo in un'istanza di un'altra classe in modo che possa essere eseguito. Nel mio esempio il genitore passa il metodo 'KeepGoing()' nel lavoratore, che periodicamente chiama per controllare con il genitore per vedere se dovrebbe continuare. Il thread di lavoro non si riavvia ogni volta: viene avviato una volta e continua fino a quando non riceve un segnale di stop. –

Problemi correlati