0

对不起,我的英语不好。希望有人建议我更好地解决我的问题。

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

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

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

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

  1. 在. ManualResetEvent_ mre_ HeavyClass当用户单击暂停按钮时,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. SleepThread创建一个内部调用的方法HeavyClass。当用户单击暂停按钮时,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. EventHandler<MainWindow> PauseThread在 UI 类内部创建一个,然后在HeavyClass. 当用户单击暂停按钮时,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
        }
    }
    

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

最后,我知道了我的问题的本质。即:哪个线程调用 Thread.Sleep() 或 WaitOne() 将被阻塞。是的,“哪个线程”,而不是“哪个类”。

现在一切对我来说都有意义。但这并不能帮助我实现目标。这让我认为我正在做看似不可能的事情。很明显,我想通过另一个线程暂停一个线程。但是另一个线程是调用任何类型的“挂起线程”的那个,所以它是被挂起的那个。我不知道如何使沉重的方法自行暂停。它正在运行,它怎么知道用户何时点击了暂停按钮?

我完全不知所措。有人请帮助我使我的应用程序按预期工作。

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

更新:如果你喜欢看我的繁重任务,其实很简单

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

1 回答 1

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();
    }
}
于 2018-07-04T05:45:07.400 回答