简体   繁体   English

如何在C#中通过另一个线程挂起线程?

[英]How to suspend a thread by another thread in C#?

Sorry for my bad English. 对不起,我的英语不好。 Hope someone suggests me a better version of my question. 希望有人给我建议一个更好的版本。

I've searched but seemed like I couldn't find the answer for my problem. 我已经搜索过,但似乎找不到我的问题的答案。

Currently, I'm writing a C# WPF app. 目前,我正在编写C#WPF应用程序。 This app will perform a heavy task in a long time. 该应用将在很长一段时间内执行繁重的任务。 So I've decided to create another class with that heavy method and pass that method to another thread. 因此,我决定使用该重型方法创建另一个类,并将该方法传递给另一个线程。 I have to create a class to do that because the heavy method takes parameters. 我必须创建一个类来执行此操作,因为Heavy方法需要参数。

I want the ability to suspend and resume that thread. 我想要暂停和恢复该线程的功能。 I've known that I should use a ManualResetEvent object or Thread.Sleep method. 我知道我应该使用ManualResetEvent对象或Thread.Sleep方法。

After many hours of trying and testing, getting confused why I always end up suspend the UI thread but the heavy thread is still running. 经过数小时的尝试和测试后,为什么我总是最终挂起UI线程却沉重的线程仍在运行,令我感到困惑。 What I've tried were: 我尝试过的是:

  1. Create a ManualResetEvent object called mre inside the HeavyClass . 创建一个ManualResetEvent称为对象mre里面HeavyClass When user click the Pause button, the UI class will call the method heavyClass.mre.WaitOne() . 当用户单击“暂停”按钮时,UI类将调用方法heavyClass.mre.WaitOne()

     class HeavyClass { // properties ManualResetEvent mre = new ManualResetEvent(false); public void HeavyRun() { //Do something takes really long time //And doesn't have any loops } } class MainWindow : Window { // properties private HeavyClass heavyClass = new HeavyClass(); private void buttonStart_Click(object sender, RoutedEventArgs e) { Thread t = new Thread(heavyClass.HeavyRun); t.Start(); } private void buttonPause_Click(object sender, RoutedEventArgs e) { heavyClass.mre.WaitOne(); } } 
  2. Create a method called SleepThread inside the HeavyClass . HeavyClass创建一个称为SleepThread的方法。 When user click the Pause button, the UI class will call the method heavyClass.SleepThread() . 当用户单击“暂停”按钮时,UI类将调用方法heavyClass.SleepThread()

     class HeavyClass { //properties ManualResetEvent mre = new ManualResetEvent(false); public void SleepThread() { Thread.Sleep(Timeout.Infinite); //mre.WaitOne(); //They are the same behavior } public void HeavyRun() { //Do something takes really long time //And doesn't have any loops } } class MainWindow : Window { // properties private HeavyClass heavyClass = new HeavyClass(); private void buttonStart_Click(object sender, RoutedEventArgs e) { Thread t = new Thread(heavyClass.HeavyRun); t.Start(); } private void buttonPause_Click(object sender, RoutedEventArgs e) { heavyClass.SleepThread(); } } 
  3. Create an EventHandler<MainWindow> PauseThread inside the UI class, then write its handle inside the HeavyClass . 在UI类中创建一个EventHandler<MainWindow> PauseThread ,然后在HeavyClass编写其句柄。 When user click the Pause button, the UI class will trigger the event PauseThread(this, this) . 当用户单击Pause按钮时,UI类将触发事件PauseThread(this, this)

     class MainWindow : Window { // properties private HeavyClass heavyClass = new HeavyClass(); public event EventHandler<MainWindow> PauseThread; private void buttonStart_Click(object sender, RoutedEventArgs e) { Thread t = new Thread(heavyClass.HeavyRun); t.Start(); } private void buttonPause_Click(object sender, RoutedEventArgs e) { PauseThread(this, this); } } class HeavyClass { // properties ManualResetEvent mre = new ManualResetEvent(false); public void HeavyRun() { MainWindow.PauseThread += (s, E) => { Thread.Sleep(Timeout.Infinite); //mre.WaitOne(); //They are the same behavior }; //Do something takes really long time //And doesn't have any loops } } 

As I said above, I always paused the UI thread and the heavy task is still running. 如上所述,我总是暂停UI线程,繁重的任务仍在运行。

And finally in the end, I've known the essence of my problem. 最后,我终于知道了问题的实质。 That is: which thread calls Thread.Sleep() or WaitOne() will be blocked. 也就是说:哪个线程调用Thread.Sleep()或WaitOne()将被阻止。 Yeah, "which thread", not "which class". 是的,“哪个线程”,而不是“哪个类”。

Everything makes sense for me now. 现在一切对我来说都很有意义。 But that doesn't help me to achieve my goal. 但这无助于我实现目标。 And that leads me to think that I am doing the seemingly impossible thing. 这使我认为我正在做看似不可能的事情。 It's clearly that I want to pause a thread by another thread. 显然,我想通过另一个线程暂停一个线程。 But that another thread is the one who calls any kinds of "suspend thread", so it is the one who is suspended. 但是另一个线程是调用任何类型的“挂起线程”的线程,因此它是挂起的线程。 I don't have any idea about how to make the heavy method to be suspended by itself. 我不知道如何使繁重的方法自己挂起。 It is running, how the hell it could know when the user click the Pause button? 它正在运行,当用户单击“暂停”按钮时如何知道?

I am at a total loss. 我完全不知所措。 Someone please help me to make my app works as expected. 有人请帮助我使我的应用程序按预期工作。

By the way, this impossible thing makes me think that I am doing things wrong way, is it? 顺便说一句,这不可能的事情使我觉得我做错事了,是吗?

UPDATE: If you like to see my heavy task, actually it is very simple 更新:如果您喜欢看我的繁重任务,实际上这很简单

    class HeavyClass
    {
        public string filePath = "D:\\Desktop\\bigfile.iso";//This file is about 10GB

        public string HeavyRun()
        {
            string MD5Hash;
            MD5 md5 = MD5.Create();
            Stream stream = File.OpenRead(filePath);
            MD5Hash = Encoding.Default.GetString(md5.ComputeHash(stream));
            return MD5Hash;
        }
    }

To make a thread suspendable, the work in the thread must be separable. 要使线程可挂起,线程中的工作必须是可分离的。 In your case md5.ComputeHash(stream) will do all the work, and there is not way to make sure that thread will suspend at a right(saft) point inside md5.ComputeHash(stream) . 在您的情况下, md5.ComputeHash(stream)将完成所有工作,并且没有办法确保线程将在md5.ComputeHash(stream)内部的right(saft)点处md5.ComputeHash(stream) So you have to rewrite HeavyClass like below. 因此,您必须像下面那样重写HeavyClass。 Please notice that those codes are not the best approach of handling a thread, and I just try to keep it as same as the original. 请注意,这些代码不是处理线程的最佳方法,我只是尝试使其与原始代码相同。

class HeavyClass
{
    MD5 _md5 = MD5.Create();
    MethodInfo _hashCoreMI = _md5.GetType().GetMethod("HashCore", BindingFlags.NonPublic | BindingFlags.Instance);
    MethodInfo _HashFinalMI = _md5.GetType().GetMethod("HashFinal", BindingFlags.NonPublic | BindingFlags.Instance);
    WaitHandle _signal;

    public void HeavyClass(WaitHandle signal)
    {
        _signal = signal;
    }

    public string HeavyRun(string filename)
    {
        byte[] buffer = new byte[4096];
        int bytesRead = 0;
        _signal.Set();

        using(FileStream fs = File.OpenRead(filename))
        {
            while(true)
            {
                bytesRead = fs.Read(buffer, 0, 4096);
                if (bytesRead > 0)
                {
                    _hashCoreMI.Invoke(_md5, new object[] { buffer, 0, bytesRead });
                }
                else
                {
                    break;
                }

                // if WaitHandle is signalled, thread will be block,
                // otherwise thread will keep running.
                _signal.WaitOne();
            }
        }

        byte[] hash = _hashFinalMI.Invoke(_md5, null);
        _md5.Initialize();

        return Encoding.ASCII.GetString(hash);;
    }
}

class MainWindow : Window
{
    private HeavyClass _heavyClass;
    private ManualResetEvent _mre;

    public MainWindow()
    {
        InitializeComponent();

        _mre = new ManualResetEvent(true);
        _heavyClass = new HeavyClass(_mer);
    }

    private void buttonStart_Click(object sender, RoutedEventArgs e)
    {
        Thread t = new Thread(heavyClass.HeavyRun("D:\\Desktop\\bigfile.iso"));
        t.Start();
    }

    private void buttonPause_Click(object sender, RoutedEventArgs e)
    {
        _mre.Reset();
    }

    private void buttonResume_Click(object sender, RoutedEventArgs e)
    {
        _mre.Set();
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM