对不起,我的英语不好。希望有人给我建议一个更好的版本。
我已经搜索过,但似乎找不到我的问题的答案。
当前,我正在编写C#WPF应用程序。该应用将在很长一段时间内执行繁重的任务。因此,我决定使用该重型方法创建另一个类,并将该方法传递给另一个线程。我必须创建一个类来这样做,因为Heavy方法需要参数。
我希望能够暂停和恢复该线程。我知道我应该使用ManualResetEvent
对象或Thread.Sleep
方法。
在经过数小时的尝试和测试之后,感到困惑的是为什么我总是最终挂起UI线程,但繁重的线程仍在运行。我尝试过的是:
在ManualResetEvent
内创建一个名为mre
的{{1}}对象。当用户单击“暂停”按钮时,UI类将调用方法HeavyClass
。
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();
}
}
内创建一个名为SleepThread
的方法。当用户单击“暂停”按钮时,UI类将调用方法HeavyClass
。
heavyClass.SleepThread()
在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
}
}
答案 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();
}
}