我必须从提供的DLL调用例程。此DLL需要2到10分钟才能运行,具体取决于运行它的PC的速度。
我已将DLL调用放入BackgroundWorker线程中,以便界面保持响应。
private object Load(string feature) {
object result = null;
using (var w = new BackgroundWorker()) {
w.WorkerReportsProgress = true;
w.WorkerSupportsCancellation = true;
w.DoWork += delegate(object sender, DoWorkEventArgs e) {
e.Result = DAL.get(feature);
};
w.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e) {
progressBar1.Visible = false;
if (e.Error != null) {
MessageBox.Show(this, e.Error.Message, "Load Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
} else {
result = e.Result;
}
};
w.RunWorkerAsync();
if (w.IsBusy) {
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.Visible = true;
}
}
return result;
}
这样可行,但我不能将此方法与其他等待其结果的调用一起调用,因为它会立即返回空值。
所以,我坚持使用ManualResetEvent实例来尝试让方法等到它返回之前实际有一个值:
private object Load(string feature) {
object result = null;
using (var w = new BackgroundWorker()) {
var mre = new ManualResetEvent(false);
w.WorkerReportsProgress = true;
w.WorkerSupportsCancellation = true;
w.DoWork += delegate(object sender, DoWorkEventArgs e) {
e.Result = DAL.get(feature);
};
w.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e) {
progressBar1.Visible = false;
if (e.Error != null) {
MessageBox.Show(this, e.Error.Message, "Model Load Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
} else {
result = e.Result;
}
mre.Set();
};
w.RunWorkerAsync();
if (w.IsBusy) {
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.Visible = true;
progressBar1.Value = 0;
const string statusRun = @"\|/-";
const string statusMsg = "Loading Data...";
int loops = 0;
do {
int index = loops++ % 4;
tsStatus.Text = statusMsg + statusRun[index].ToString(); // '\', '|', '/', '-'
} while (!mre.WaitOne(200));
}
}
return result;
}
但是,这样做会导致我的所有CPU时间花费在ManualResetEvent的WaitOne
方法上,并且永远不会调用Set()
触发器。
之前是否有人遇到此行为并找到了一个好的解决方法?
我在过去为它创建了一个解决方法,但它涉及创建第二个线程来运行WaitOne
方法,以便第一个线程可以处理DoWork
代码。
答案 0 :(得分:2)
这样可行,但我不能将此方法与其他等待其结果的调用一起调用,因为它会立即返回空值。
这就是发明async
和await
个关键字的原因。没有简单的解决方案可以阻止线程。
由于你使用的DLL显然不提供使用任何异步编程模式(如BeginOperation
/ EndOperation
或基于Task的 - 基于异步编程)实现的方法,所以你'坚持使用单独的工作线程。
你能做的是:
像往常一样开始BackgroundWorker
或Thread
,然后立即返回。不要继续执行需要冗长DLL进程的返回值的操作。完成BackgroundWorker
或Thread
后,让它报告进度,并在ProgressChanged
事件处理程序中,您可以检索冗长的DLL进程的结果并继续操作。或者,您可以使用RunWorkerCompleted
事件(实际上可能是更好的选择)。
与此同时,您可能必须禁用所有可能再次启动冗长DLL进程的控件,否则会在进程运行时发出无效操作。正如亨克霍尔特曼写的那样,不要像那样处置BackgroundWorker
。