2012-05-06 91 views
12

如何暫停/恢復線程?一旦我Join()一個線程,我無法重新啓動它。 那麼,如何啓動一個線程,並在按下「暫停」按鈕時暫停,並在按下恢復按鈕時恢復它?如何暫停/恢復線程

此線程所做的唯一事情是在標籤控件中顯示一些隨機文本。

+1

你可以在你的問題中加入你的代碼 – Adil

+0

目前沒有代碼。我仍然在思考如何實施這個我腦海中的項目。我可以告訴你,我從使用線程的工作中獲得的經驗知道,一旦線程與主線程連接在一起,就無法恢復/重啓線程。 – Yustme

回答

16

也許ManualResetEvent是一個不錯的選擇。 一個簡短的例子:

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

在VirtualBox XP中不起作用。不知道爲什麼? – zionpi

+0

我很想使用[_reserved keyword_](https://msdn.microsoft.com/en-us/library/x53a06bb.aspx)作爲變量名稱,但該解決方案對我來說完全適用於巨魔:-) – MDMoore313

+0

我沒有定義事件(♡Rx♡),所以我不知道'event'是一個關鍵字:-) –

5

我可以建議你閱讀Threading in C#, by Joe Albahari,特別Suspend and Resume部分:

線程可以明確地暫停,並通過過時方法Thread.Suspend和Thread.resume恢復。這種機制與阻塞機制完全分開。兩個系統都是獨立的,並行運行。

一個線程可以掛起自己或另一個線程。調用Suspend會導致線程暫時進入SuspendRequested狀態,然後在達到垃圾收集的安全點時進入Suspended狀態。從那裏,它只能通過另一個調用其Resume方法的線程來恢復。恢復將只在暫停的線程上工作,而不是在阻塞的線程上工作。

從.NET 2.0開始,暫停和恢復已被棄用,由於任意掛起另一個線程的內在危險而不鼓勵使用它們。如果暫掛關鍵資源上的鎖定線程,則整個應用程序(或計算機)可能會死鎖。這比調用Abort更危險 - 這會導致任何這樣的鎖都會通過finally塊中的代碼被釋放(至少在理論上)。

SuspendRequested state

+0

你的區塊引用區分了掛起的線程和被阻塞的線程,但該圖似乎沒有做出區分。這是故意的嗎? – Tung

+0

@gliderkite,請參閱我對Adil有關顯示代碼的回覆。 – Yustme

+0

@Tung由於某種原因,例如當正在沉睡或等待另一個線程通過Join或EndInvoke結束時,線程被視爲被阻止。在代碼中:'bool blocked =(someThread.ThreadState&ThreadState.WaitSleepJoin)!= 0;'。那麼爲什麼圖中沒有區別。見[this](http://www.albahari.com/threading/part2.aspx#_Blocking)。 – gliderkite

2

手動暫停和恢復線程並不是最好的主意。但是,您可以使用線程同步基元很容易地模擬此行爲(如ManualResetEvent

看一看this question,可能會發現它有幫助。

但我相信你可以很容易地通過使用計時器來實現「在標籤控件中顯示隨機文本」的目標。

下面是使用DispatcherTimer

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

一個簡單的例子,您可以通過調用timer.Stop()再次停頓,然後timer.Start()恢復。

+0

嗨,這看起來不錯,我可以把這個代碼放在一個單獨的線程?因爲我不想阻塞GUI線程。 – Yustme

+0

@Yustme:AFAIK'DispatcherTimer'的代碼在GUI線程上執行。如果你想單獨使用'System.Threading.Timer'。 – Tudor

+0

都鐸王朝是正確的,所以如果你有一些'沉重'的代碼需要在定時器的時鐘上執行,使用單獨的線程。但是如果你只需要更新文本塊,使用DispatcherTimer就可以,並且不會阻塞你的UI。 –

1

這裏有兩種方法適用於我。兩者都假定工作線程有它自己的處理循環。

  1. 紛紛跟帖調用回調請求允許繼續下去
  2. 有父調用的線程的類中的方法,以表示它

下面的控制檯應用程序示例說明了這兩種方法,使用暫停/繼續的回調以及停止的工人方法。回調方法的另一個優點是在檢查是否允許繼續時傳回狀態更新也很方便。

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

嗨,對於我的生活,我無法理解代表。我可以想出如何停止線程,但我不明白你是如何重新啓動它? – Zev

+0

@Zev - 委託是一個方法的佔位符,所以你可以將它傳遞給另一個類的實例,以便它可以執行它。在我的示例中,父節點將'KeepGoing()'方法傳遞給worker,它會週期性地調用以檢查父節點是否應該繼續。工作線程不會每次都重新啓動 - 它會啓動一次並持續運行,直到它收到停止信號。 –