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

时间:2018-07-02 05:57:04

标签: c# wpf multithreading winapp

对不起,我的英语不好。希望有人给我建议一个更好的版本。

我已经搜索过,但似乎找不到我的问题的答案。

当前,我正在编写C#WPF应用程序。该应用将在很长一段时间内执行繁重的任务。因此,我决定使用该重型方法创建另一个类,并将该方法传递给另一个线程。我必须创建一个类来这样做,因为Heavy方法需要参数。

我希望能够暂停和恢复该线程。我知道我应该使用ManualResetEvent对象或Thread.Sleep方法。

在经过数小时的尝试和测试之后,感到困惑的是为什么我总是最终挂起UI线程,但繁重的线程仍在运行。我尝试过的是:

  1. ManualResetEvent内创建一个名为mre的{​​{1}}对象。当用户单击“暂停”按钮时,UI类将调用方法HeavyClass

    heavyClass.mre.WaitOne()
  2. 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(); } } 内创建一个名为SleepThread的方法。当用户单击“暂停”按钮时,UI类将调用方法HeavyClass

    heavyClass.SleepThread()
  3. 在UI类中创建一个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(); } } ,然后在EventHandler<MainWindow> PauseThread中编写其句柄。当用户单击“暂停”按钮时,UI类将触发事件HeavyClass

    PauseThread(this, this)

如上所述,我总是暂停UI线程,而繁重的任务仍在运行。

最后,我终于知道了问题的实质。也就是说:哪个线程调用Thread.Sleep()或WaitOne()将被阻止。是的,“哪个线程”,而不是“哪个类”。

现在一切对我来说都很有意义。但这无助于我实现目标。这使我认为我正在做看似不可能的事情。显然,我想通过另一个线程暂停一个线程。但是另一个线程是调用任何类型的“挂起线程”的线程,因此它是挂起的线程。我不知道如何使繁重的方法自己挂起。它正在运行,当用户单击“暂停”按钮时怎么知道?

我全神贯注。有人请帮助我使我的应用程序按预期运行。

顺便说一句,这不可能的事情使我觉得我做错事了,对吗?

更新:如果您喜欢看我的繁重任务,实际上这很简单

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

1 个答案:

答案 0 :(得分:0)

要使线程可挂起,线程中的工作必须是可分离的。在您的情况下,md5.ComputeHash(stream)将完成所有工作,并且无法确保线程将在md5.ComputeHash(stream)内部的右(安全)点处挂起。因此,您必须像下面那样重写HeavyClass。请注意,这些代码并不是处理线程的最佳方法,我只是尝试使其与原始代码保持相同。

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