I’ve got a threaded invoke call that never returns.
The thread runs just fine right up until I call the line that ways, “owner.Invoke(methInvoker);“
When debugging, I can slowly step, step, step, but once I hit owner.Invoke… it’s Over!
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();
}
}
}
FYI: This is a custom class that mimics the BackgroundWorker class, which is not available on controls that do not have Forms.
Thinking Invoke might not be required, I manually stepped the cursor in the debugger over that part of the code and tried calling ProgressChanged directly, but VS2010’s debugger threw a cross thread exception.
EDIT:
Due to the first 3 comments I have received, I wanted to update with my ProgressChanged method:
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;
};
There is a breakpoint on the first line of the anonymous method, but it never gets hit either.
EDIT 2
Here is a more complete listing of the call to the thread:
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();
}
}
}
I hope this isn’t overkill! I hate presenting too much information, because then I get people critiquing my style. 🙂
That’s a guaranteed deadlock. The delegate you pass to Control.Begin/Invoke() can only run when the UI thread is idle, having re-entered the message loop. Your UI thread isn’t idle, it is blocked on the WaitOne() call. That call can’t complete until your worker thread completes. Your worker thread can’t complete until the Invoke() call is completed. That call can’t complete until the UI thread goes idle. Deadlock city.
Blocking the UI thread is fundamentally a wrong thing to do. Not just because of .NET plumbing, COM already requires it to never block. That’s why BGW has a RunWorkerCompleted event.