C#: Method Invoke never returns - c#

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. :)

worker.RunWorkerAsync(tList[i]);
//...
mre.WaitOne();
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.

It is likely that you have deadlocked the UI and worker threads. Control.Invoke marshals the execution of a delegate onto the UI thread by posting a message to the UI thread's message queue and then waits for that message to be processed which in turn means the execution of the delegate has to complete before Control.Invoke returns. But, what if your UI thread is busy doing something else beside dispatching and processing messages? I can see from your code that a ManualResetEvent may be in play here. Is your UI thread blocked on a call to WaitOne by chance? If so that could definitely be the problem. Since WaitOne does not pump messages it will block the UI thread which will lead to a deadlock when Control.Invoke is called from your worker thread.
If you want your ProgressChanged event to behave like it does with BackgroundWorker then you will need to call Control.Invoke to get those event handlers onto the UI thread. That is the way BackgroundWorker works anyway. Of course, you do not have to mimic the BackgroundWorker class exactly in that respect as long as you are prepared to have the callers do their own marshaling when handling the ProgressChanged event.

Related

Progress Bar with Label — Unable to Update Label in ProgressChanged

I have a background worker with a long running task. The task goes through a list of files and I want to update the user with which file we are on. I have a tool strip that has a label named panel1.text. The progress bar is working however the label is not changing in my ProgressChanged method i.e. It should say Processing File1 then change to Processing File2, but it stays on the default of Processing.
private void btnProcess_Click(object sender, EventArgs e)
{
toolStripProgressBar1.Visible = true;
toolStripProgressBar1.Maximum = 1000000000;
panel1.Text = "Processing "; // this appears properly
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(processFiles);
worker.ProgressChanged += ProgressChanged;
worker.RunWorkerAsync();
while (worker.IsBusy)
{
// the reason for this is because nothing can happen until the processing is done
toolStripProgressBar1.Increment(1);
}
// more processing
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
panel1.Text = "Processing "+ e.UserState.ToString(); <<<---- This is Not Updating panel1.Text but it evaluates properly
}
private void processFiles(object sender, EventArgs e)
{
int retVal = 0;
foreach (string fileName in listBox1.Items)
{
ProgressChangedEventArgs ea = new ProgressChangedEventArgs(1,fileName);
ProgressChanged(this, ea);
// do more processing
}
}
I would appreciate any help.
You are using the same thread, which is being blocked by another process. You need to use a Task to create a new thread and possibly use Dispatcher.BeginIvoke if the control is on the other thread. Make sure whatever Button Click, etc is happening is marked with the Async keyword as well to make it Asynchronous.
Example:
Await Task mytask = Task.Run(() =>
for(var i = 0; i < 1000; i++)
{
Label.Dispatcher.BeginInvoke( () =>
UpdateMe(int i, LabelClass/Component class/component)});
Then inside the Label Class or wherever the label is:
Public void UpdateMe(int i, LabelClass class)
{
class.label.content = Cint((i/Total)*100);
Thread.Sleep(500);
}
There are other ways to do it as well such as Binding the value to the UI, but this will give you a better understanding of why its not working and how things work with other threads.
If you want to really get a visual understanding call:
`Console.WriteLine($"Current Thread ID: System.Threading.Thread.CurrentThread.ManagedThreadId}");`
Right before you go into the Task---it will give you the main thread ID
Then inside the Task call it again...this will give you the secondary thread ID.
Then Right before the Dispatcher call:
Console.WriteLine($"Do I have access to the label on this thread? {Label.Dispatcher.CheckAccess()}";
If you have access it will display True, if not it will display False...In your case it will display false because its owned by the other thread, but you can use the Dispatcher to be able to do work on that thread while in another thread...
Also, I recommend you not use Background Worker and use Tasks instead...this explains why in depth...basically Tasks do everything Background workers do and more, have less issues and are easier to work with...
http://blog.stephencleary.com/2013/09/taskrun-vs-backgroundworker-conclusion.html
As already commented by Ivan, remove the while loop while (worker.IsBusy) as it's blocking the UI thread to process further. As well, you should enable the WorkerReportsProgress to true
worker.WorkerReportsProgress = true;
worker.ProgressChanged += ProgressChanged;
while (!worker.IsBusy)
{
worker.RunWorkerAsync();
}
Per your comment, move those later processing to BackgroundWorker.RunWorkerCompleted Event

Background Worker is Running but Doesn't Print anything

I have a BackgroundWorker called bgw to whom I pass my custom Form called LogBoxForm to. The custom Form job is simply to print something on it.
LogBoxForm logBox = new LogBoxForm(); //both declared in the main Form
BackgroundWorker bgw = new BackgroundWorker();
In the main Form's Load event initiated the two bgw events: DoWork and RunWorkerCompleted like this
bgw.DoWork += bgw_DoWork;
bgw.RunWorkerCompleted += bgw_RunWorkerCompleted;
And then when I pressed a Button named button9, the bgw will be ran as directed by this code
//Background worker
BackgroundWorker bgw = new BackgroundWorker();
private void button9_Click(object sender, EventArgs e) {
if (bgw.IsBusy)
return;
bgw.RunWorkerAsync(logBox);
}
void bgw_DoWork(object sender, DoWorkEventArgs e) {
LogBoxForm lbf = e.Argument as LogBoxForm;
try {
for (int i = 0; i < 5; ++i) {
lbf.WriteTimedLogLine("loop " + (i + 1).ToString());
Thread.Sleep(1000);
}
} catch (Exception exc) {
throw exc;
}
}
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
logBox.WriteTimedLogLine("Completed!");
if (e.Error != null)
logBox.WriteTimedLogLine(e.Error.ToString());
}
It stops in the catch line This is the error message I get:
System.InvalidOperationException: Cross-thread operation not valid:
Control 'richTextBoxAll' accessed from a thread other than the thread
it was created on.
I am pretty new user for BackgroundWorker and may not really be aware how all these can happen. I am hoping that some more experienced can tell what is wrong with my code. I am looking forward for your guidance.
This should be a classic case of Thread Affinity. Since the BackgroundWorker runs on a different thread other than the UI thread you need to call .Invoke. Checkout this link here to see an example of an extension method that encapsulates thread-safe invocation.
With this extension method you can write WinForms thread-safe code like this:
this.ThreadSafeInvoke(() => logBox.WriteTimedLogLine("loop " + (i + 1).ToString()));
The key here is that since your execution on a different thread, the .InvokeRequired bool will return true and then you'll execute the Action passed into the ThreadSafeInvoke via the .Invoke -- which will marshal back to the UI thread.
If you do not want to have an extension method simply do the following instead:
this.Invoke(new MethodInvoker(() =>
logBox.WriteTimedLogLine("loop " + (i + 1).ToString())));
The advantages to the extension method are obvious. I hope you find this helpful.

BackgroundWorker not exiting loop

I have an BackgroundWorker:
BackgroundWorker worker;
private void Form1_Load(object sender, EventArgs e)
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.ProgressChanged +=
new ProgressChangedEventHandler(worker_ProgressChanged);
worker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
}
DoWork Event
void worker_DoWork(object sender, DoWorkEventArgs e)
{
int percentFinished = (int)e.Argument;
while (!worker.CancellationPending && percentFinished < 100)
{
percentFinished++;
worker.ReportProgress(percentFinished);
//here I start my operation
//operation....
//operation end
}
e.Result = percentFinished;
}
Progresschanged
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
Completed
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Asynchroner Thread kam bis zum Wert:
"+e.Result.ToString());
btnStartEnd.Text = "Starten";
}
And finally my button:
private void btnStartEnd_Click(object sender, EventArgs e)
{
if (worker.IsBusy)
{
worker.CancelAsync();
btnStartEnd.Text = "Starten";
}
else
{
if (progressBar1.Value == progressBar1.Maximum)
{
progressBar1.Value = progressBar1.Minimum;
}
worker.RunWorkerAsync(progressBar1.Value);
btnStartEnd.Text = "Stoppen";
}
}
This code works but I get a loop for my operations until the percentage is 100, so the operation starts 100 times and so takes 100 times longer.
The goal should be that the operation only starts one time and the percentage counts from 1-100.
Maybe I understand something wrong, but how does the worker know how far the operation is done? That value should be send to the progress bar for visualisation.
Normally you won’t add the loop inside the DoWork method
If you want to load for example 100 files from the file system and report the progress it could look like that:
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for(int i = 0; i < 100; i++)
{
// Load file and do something with the content
...
// Report the progress which causes the ProgressChanged event to be fired
// And update progressbar with the UI thread
worker.ReportProgress(i);
}
}
If you only have one long running operation that needs to be executed inside the DoWork method it needs to be executed asynchronously
Here is one example how you could call an action asynchronously in .NET:
Action action = () =>
{
for(int i = 0; i <100; i++)
{
Console.WriteLine(String.Format("Step {0} of long running operation", i));
Thread.Sleep(1000);
}
};
var r = action.BeginInvoke(null, null);
while(!r.IsCompleted)
{
Console.WriteLine("Waiting...");
Thread.Sleep(100);
}
However in .NET there are many more ways to do it. See for example:
http://msdn.microsoft.com/en-us/library/jj152938(v=vs.110).aspx for
Async patterns in .NET
http://msdn.microsoft.com/de-de/library/hh191443.aspx for Async
programming with await/async (new in .NET 4.5)
Maybe I understand something wrong, but how does the worker know how far the operation is done? That value should be send to the progress bar for visualisation.
The BackgroundWorker class doesn't know anything about your operations and about its progress. It's your job to determine when to report the progress.
In general the workflow for a background worker looks like this:
UI thread calls RunWorkerAsync.
DoWork event handler is called on a different thread. During the event handler you can report progress using the ReportProgress method
If you report a progress then the ProgressChanged event handler is called on the UI thread. Here you can update a progress bar for example.
When your event handler for the DoWork event exits the RunWorkerComplete event is raised.
Now why does every example for the BackgroundWorker has a for-loop? Because it's very easy to write, and measuring progress for a for-loop is trivial. Unfortunately this quite often isn't useful for different kind of operations.
If your long running operation processes N files then it's pretty obvious that you can update the progress bar after every item by 1/N. That's what the for-loop example does.
But if you only have one long running operation then you simply don't have any chance to get the progress unless the operation itself supports reporting it or if you can somehow estimate the progress.
The BackgroundWorker can't magically give a long running operation a progress bar. It only enables you to run the operation in the background.

backgroundworker not firing RunWorkerCompleted event

i have a web application with a search form. The form is being used to search through my own database, but also at other databases through their API service. To provide a fast response, i have decided to split the work in main thread and the backgroundworker service.
Here is my method which runs the search task.
// search through other API
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.WorkerSupportsCancellation = true;
worker.WorkerReportsProgress = true;
worker.RunWorkerAsync(form);
// search in my api
...
// wait for API results
while (worker.IsBusy) {
for (sbyte i = 0; i < sbyte.MaxValue; i++) ;
}
// handle data and show to user
And the methods are
private void worker_DoWork(object sender, DoWorkEventArgs e) {
Forms f = e.Argument as Forms;
if (f != null) {
List<ApiResponse> result = GetDataFromOtherApi(f);
e.Result = result;
}
else e.Result = null;
}
The GetDataFromOtherApi() method works fine in the single thread test. It also works when running the backgroundworker thread. I have set a breakpoint at the end and e.Result has values which i was looking for.
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
List<ApiResponse> reply = e.Result as List<ApiResponse>;
if (reply == null) ar = null;
else if (reply.Any()) ar = reply;
else ar = null;
}
the ar is a variable that the main thread can access to after the while loop has done with running. But, when placing a breakpoint at the start, it never stops there, the loop itself is continuous to the infinity ... It means that the event is never being fired.
Am i missing something ?
The BackgroundWorker progress & completed events are synchronised to run on the UI thread. I'd guess as you're blocking that thread, they'll never get fired.

Cancel backgroundworker

I have UI which displaying status of long-running operations (downloading some text files from ftp) . For my purposes I use backgroundworker and I can't cancel operation.
void worker_DoWork( object sender, DoWorkEventArgs e )
{
try
{
int rowIndex = (int)e.Argument;
//begin UI update
StartWaitingBar(rowIndex);
//get provider id cell
GridViewDataRowInfo row = _proivderGridView.Rows[rowIndex];
GridViewCellInfo provIdCell = row.Cells[ "ProviderId" ];
var providerData = GetProviderData(Convert.ToInt32( provIdCell.Value));
var provider = ProviderFactory.CreateProvider(providerData);
provider.Synchronize();
e.Result = rowIndex;
}
catch (Exception exception)
{
return;
}
}
And code for worker creation:
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.WorkerSupportsCancellation = true;
worker.RunWorkerAsync(args.RowIndex);
_syncWorkers.Add(providerId,worker);
...
var worker = _syncWorkers[providerId];
if(worker.IsBusy)
{
worker.CancelAsync();
}
else
{
worker.RunWorkerAsync(args.RowIndex);
}
Solution provided here seems not working for me beacuse it works for recurring operations (for which background worker is created, I suppose). Do I have to use threads(abort and join) for my purposes because I should provide possibilities for user to cancel long-running operation?
Need your advice.
Thanks in advance.
You cannot use Backgroundworker.CancelAsync() to cancel a long running I/O action. Like rifnl answered, the DoWork has to check worker.CancellationPending and set e.Cancel.
But you shouldn't use Thread.Abort() either. It could destabilize your process.
The solution you need has to come from provider.Synchronize(); somehow.
PS: and catch { return; } is horrible. Remove the entire try/catch and let the Bgw handle exceptions.
You've got to check e.Cancel within your DoWork method, which is missing from your code-snippet, but you've got to change your download method to an async call too, you're calling the method and wait for the answer within the dowork. Which is possible, but it won't check for the cancel flag in mean time.
Check the solution you posted (line 3):
void worker_DoWork(object sender, DoWorkEventArgs e)
{
while(!e.Cancel)
{
// do something
}
_resetEvent.Set(); // signal that worker is done
}

Categories

Resources