【问题标题】:how to pause/resume a thread如何暂停/恢复线程
【发布时间】:2012-05-06 10:06:24
【问题描述】:

如何暂停/恢复线程?一旦我Join() 一个线程,我就无法重新启动它。 那么如何在按下“暂停”按钮时启动线程并使其暂停,并在按下恢复按钮时恢复它?

这个线程唯一做的就是在标签控件中显示一些随机文本。

【问题讨论】:

  • 你可以在你的问题中添加你的代码
  • 暂时没有代码。我仍在考虑如何实施我脑海中的这个项目。我只能告诉你,根据我的线程工作经验,一旦线程与主线程连接,我就无法恢复/重新启动它。

标签: c# wpf multithreading resume


【解决方案1】:

也许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
   }
}

【讨论】:

    【解决方案2】:

    我建议你阅读Threading in C#, by Joe Albahari,尤其是Suspend and Resume 部分:

    可以通过已弃用的方法 Thread.Suspend 和 Thread.Resume 显式挂起和恢复线程。这种机制与阻塞机制是完全分开的。两个系统都是独立的并且并行运行。

    一个线程可以挂起自己或另一个线程。调用 Suspend 会导致线程短暂进入 SuspendRequested 状态,然后在达到垃圾收集安全点时,它进入 Suspended 状态。从那里,它只能通过另一个调用其 Resume 方法的线程来恢复。 Resume 仅适用于挂起的线程,而不适用于阻塞的线程。

    从 .NET 2.0 开始,Suspend 和 Resume 已被弃用,不鼓励使用它们,因为任意挂起另一个线程所固有的危险。如果持有关键资源锁的线程被挂起,整个应用程序(或计算机)可能会死锁。这比调用 Abort 危险得多——后者会通过 finally 块中的代码释放任何此类锁(至少在理论上)。

    【讨论】:

    • 您的块引用区分了挂起的线程和阻塞的线程,但该图似乎没有做出这种区分。这是故意的吗?
    • @Tung 当一个线程的执行由于某种原因被暂停时,它被认为是阻塞的,例如当睡眠或等待另一个通过 Join 或 EndInvoke 结束时。在代码中:bool blocked = (someThread.ThreadState & ThreadState.WaitSleepJoin) != 0;。那么为什么图中没有区别。见this
    • @gliderkite,我知道,这就是为什么我要问如何做到这一点,这就是为什么目前没有代码可以显示,仅仅是因为我还没有开始。只是提醒您,在您的第一个答案的第一行中,您指向“没有可用的代码”,我正在解释原因。
    • @Yustme 我明白了,只是误会。
    • @Tung 可能(“如果通过 (deprecated) Suspend 方法暂停线程的执行,则不会认为线程被阻塞”)。
    【解决方案3】:

    手动挂起和恢复线程并不是最好的主意。但是,您可以使用线程同步原语(如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() 以恢复。

    【讨论】:

    • 嗨,这看起来不错,我可以把这段代码放在一个单独的线程中吗?因为我不想阻塞 GUI 线程。
    • @Yustme:AFAIK DispatcherTimer 的代码在 GUI 线程上执行。如果你想要一个单独的线程,请使用System.Threading.Timer
    • Tudor 是对的,所以如果你有一些需要在计时器的滴答声上执行的“繁重”代码,请使用单独的线程。但是如果你只需要更新文本块,使用DispatcherTimer 就可以了,不会阻塞你的用户界面。
    【解决方案4】:

    这里有两种对我有用的方法。两者都假设工作线程有自己的处理循环。

    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;
            }
        }
    }
    

    【讨论】:

    • 嗨,我一生都无法理解代表。我可以弄清楚如何停止线程,但我不明白您是如何重新启动它的?
    • @Zev - 委托只是方法的占位符,因此您可以将它传递给另一个类的实例,以便它可以执行它。在我的示例中,父级将KeepGoing() 方法传递给worker,它会定期调用该方法以与父级进行核对以查看它是否应该继续运行。工作线程不会每次都重新启动 - 它会启动一次并继续运行,直到收到停止信号。
    猜你喜欢
    • 1970-01-01
    • 2011-08-02
    • 1970-01-01
    • 2015-02-21
    • 2012-07-13
    • 1970-01-01
    • 1970-01-01
    • 2011-04-01
    相关资源
    最近更新 更多