C#:方法调用从不返回
我有一个线程调用,永远不会返回 线程一直正常运行,直到我这样调用行,“C#:方法调用从不返回,c#,multithreading,backgroundworker,invoke,C#,Multithreading,Backgroundworker,Invoke,我有一个线程调用,永远不会返回 线程一直正常运行,直到我这样调用行,“owner.Invoke(methInvoker);” 调试时,我可以慢慢地一步一步地进行,但一旦我点击了owner.Invoke。。。结束了 Control owner; public event ReportCeProgressDelegate ProgressChanged; public void ReportProgress(int step, object data) { if ((owner != null
owner.Invoke(methInvoker);
”
调试时,我可以慢慢地一步一步地进行,但一旦我点击了owner.Invoke
。。。结束了
Control owner;
public event ReportCeProgressDelegate ProgressChanged;
public void ReportProgress(int step, object data) {
if ((owner != null) && (ProgressChanged != null)) {
if (!CancellationPending) {
ThreadEventArg e = new ThreadEventArg(step, data);
if (owner.InvokeRequired) {
MethodInvoker methInvoker = delegate { ProgressChanged(this, e); };
owner.Invoke(methInvoker);
} else {
ProgressChanged(this, e);
}
} else {
mreReporter.Set();
mreReporter.Close();
}
}
}
仅供参考:这是一个自定义类,模仿BackgroundWorker
类,该类在没有表单的控件上不可用
考虑到可能不需要调用,我手动将调试器中的光标移到该部分代码上,并尝试直接调用ProgressChanged
,但VS2010的调试器抛出了一个跨线程异常
编辑:
由于我收到的前3条评论,我想用我的ProgressChanged
方法更新:
worker.ProgressChanged += delegate(object sender, ThreadEventArg e) {
if (progressBar1.Style != ProgressBarStyle.Continuous) {
progressBar1.Value = 0;
object data = e.Data;
if (data != null) {
progressBar1.Maximum = 100;
}
progressBar1.Style = ProgressBarStyle.Continuous;
}
progressBar1.Value = e.ProgressPercentage;
};
匿名方法的第一行有一个断点,但它也不会被命中
编辑2
下面是对线程调用的更完整列表:
List<TableData> tList = CollectTablesFromForm();
if (0 < tList.Count) {
using (SqlCeReporter worker = new SqlCeReporter(this)) {
for (int i = 0; i < tList.Count; i++) {
ManualResetEvent mre = new ManualResetEvent(false);
worker.StartThread += SqlCeClass.SaveSqlCeDataTable;
worker.ProgressChanged += delegate(object sender, ThreadEventArg e) {
if (progressBar1.Style != ProgressBarStyle.Continuous) {
progressBar1.Value = 0;
object data = e.Data;
if (data != null) {
progressBar1.Maximum = 100;
}
progressBar1.Style = ProgressBarStyle.Continuous;
}
progressBar1.Value = e.ProgressPercentage;
};
worker.ThreadCompleted += delegate(object sender, ThreadResultArg e) {
Cursor = Cursors.Default;
progressBar1.Visible = false;
progressBar1.Style = ProgressBarStyle.Blocks;
if (e.Error == null) {
if (e.Cancelled) {
MessageBox.Show(this, "Save Action was Cancelled.", "Save Table " + tList[i].TableName);
}
} else {
MessageBox.Show(this, e.Error.Message, "Error Saving Table " + tList[i].TableName, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
mre.Set();
};
worker.RunWorkerAsync(tList[i]);
progressBar1.Value = 0;
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.Visible = true;
Cursor = Cursors.WaitCursor;
mre.WaitOne();
}
}
}
List tList=CollectTablesFromForm();
如果(0<列表计数){
使用(SqlCeReporter worker=newsqlcereporter(this)){
for(int i=0;i
我希望这不是太过分了!我讨厌提供太多的信息,因为这样我会让人们批评我的风格 用户界面和工作线程可能已死锁<代码>控制。调用通过将消息发布到UI线程的消息队列,将委托的执行封送到UI线程,然后等待该消息被处理,这反过来意味着委托的执行必须在
控制之前完成。调用
返回。但是,如果您的UI线程除了发送和处理消息外,还忙于做其他事情,该怎么办?我可以从您的代码中看出,这里可能有一个ManualResetEvent
。在调用WaitOne
时,您的UI线程是否碰巧被阻塞?如果是这样,那肯定是个问题。由于WaitOne
不传输消息,因此它将阻止UI线程,这将导致从工作线程调用Control.Invoke
时出现死锁
如果希望ProgressChanged
事件的行为与BackgroundWorker
类似,则需要调用控件。调用将这些事件处理程序放到UI线程上。这就是BackgroundWorker
的工作方式。当然,您不必在这方面完全模仿BackgroundWorker
类,只要您准备好让调用方在处理ProgressChanged
事件时自己进行封送处理
worker.RunWorkerAsync(tList[i]);
//...
mre.WaitOne();
这肯定是一个僵局。传递给Control.Begin/Invoke()的委托只能在UI线程空闲并重新进入消息循环时运行。您的UI线程不是空闲的,它在WaitOne()调用中被阻塞。在工作线程完成之前,该调用无法完成。在完成Invoke()调用之前,工作线程无法完成。在UI线程空闲之前,该调用无法完成。死锁之城
阻塞UI线程从根本上来说是错误的。不仅仅是因为.NET管道,COM已经要求它永不阻塞。这就是为什么BGW有一个RunWorkerCompleted事件。调用的Methodos是一个完全不同的过程,在方法的开头添加断点,这样u就可以进入它。事件循环可能会被绑定到其他地方。我会检查另一个处理程序中是否有任何阻塞。您是否在调试器挂起时暂停调试器,转到“线程”调试窗口,双击每个线程,查看其调用堆栈和执行位置,并查找死锁(例如,代码在多个位置暂停在“等待”或“等待所有”)2)一般操作。在应用程序冻结之前,请注意GUI线程的线程ID,并特别查看它在冻结之后执行的操作。第一个代码块中引用的ManualResetEvent
仅局限于自定义线程类…到目前为止,我还没有真正使用它。如果我需要的话,这似乎是取消一个非常漫长的过程的好方法。另一方面,主线程(UI)也有自己的ManualResetEvent
。代码块的任务是在表单上保存一个或多个数据表,我希望防止多个表同时保存到本地SQL CE数据库。见编辑2的代码(张贴不久)。这也是布赖恩的评论让我思考的。看起来我需要重新设计类的各个部分。好吧,删除mre.WaitOne()调用即可。然后开始思考