I'm using several external services when items in my application are completed. Each service I must contact gets it's own BackgroundWorker to perform its task. I keep track of how many workers completed their work to determine if there were any errors. The issue I'm running into is that RunWorkerCompleted doesn't always get fired.
Here is what I have:
var exceptionsCaught = new List<Exception>();
var completedCount = 0;
foreach(var notificationToSend in NotificationQueue)
{
var backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += (sender, b) => notificationToSend();
backgroundWorker.RunWorkerCompleted += (sender, args) =>
{
Console.WriteLine("Done.");
if(args.Error != null)
{
exceptionsCaught.Add(args.Error);
Console.WriteLine("Error! {0}", args.Error.Message);
}
completedCount++;
};
backgroundWorker.RunWorkerAsync();
}
var exceptionCheckerWorker = new BackgroundWorker();
exceptionCheckerWorker.DoWork += (sender, b) =>
{
while (true)
{
if (completedCount != NotificationQueue.Count) continue;
if (exceptionsCaught.Any())
{
var notificationExceptionMessage =
new NotificationExceptionsMessage(exceptionsCaught,
_notificationGeneratedPath);
_eventAggregator.Publish(notificationExceptionMessage);
break;
}
break;
}
};
exceptionCheckerWorker.RunWorkerAsync();
What am I missing?
When it fails to get called I see The thread 0x2e0 has exited with code 0 (0x0). in my console.
I have changed the way I increment my count by using Interlocked but that hasn't changed anything. I know the event isn't being called because I have a breakpoint set.
Any ideas?
Are you sure that the RunWorkerCompleted event is not raised? The most likely cause is that you're not incrementing the completedCount field properly. Try using the Interlocked methods:
Interlocked.Increment(ref completedCount);
You have race condition when incrementing completedCount. You should use Interlocked.Increment
I don't know but you are writing a multi-threaded procedure that shears global data without a single lock, something surly is going wrong.
Related
I am trying to discover the best way of avoiding UI-Lockups when you are doing a lot of updates to the UI at once.
The basic premise is that on start up my tool runs a perforce FSTAT on the within a background worker. This generates a very large list of files and their information. Once this is completed, in its RunWorkerCompleted function, I then propagate this information to the UI inside of a TreeView.
This however, involves lots of property updates! Depending on the number of files that its propagating to. It can be 5000+ files. This completely locks up the UI for about 3-5 seconds.
I was wondering if I can asynchronously update the UI, such that I say, propagate 10-20 files at once & Still let the UI thread continue to update so that its still responsive.
Thank you.
If you are updating information inside of the TreeView using property bindings you could set your Binding.IsAsync flag to true. If you aren't updating the values using bindings then that might be something to look into.
Binding.IsAsync Property
Another option would be to update all your properties but, to not call the PropertyChanged event for the property (Assuming you are using INotifyPropertyChanged to update your bindings) until all your data has been changed and then call the PropertyChanged event for each of your properties on a Task so, it is still Async but even with 5000+ binding updates it should not take 3-5 seconds.
lots of suggestions you made finally got me to a good answer. Here is my code below. Basically we can use ReportProgress to allow the UI to update-while-running. Then adjust for how often we want this to happen. Here is my solution below.
The key is that PropegateMetaData is called for every N number of items (I specified 25). Then the list is emptied.
This will call report progress for every 25 items, then continue on. And eventually pass the rest to WorkerCompleted.
public static void Refresh(List<string> refreshPaths)
{
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
List<string> filesPath = null;
if (refreshPaths == null)
{
filesPath = DatabaseViewModel.Instance.Records.Select(record => record.Filepath).ToList();
}
else
{
filesPath = new List<string>(refreshPaths);
}
if (m_Repository != null && filesPath.Count > 0)
{
IList<FileSpec> lfs = new List<FileSpec>();
int index = 0;
foreach (DataRecord rec in DatabaseViewModel.Instance.Records)
{
lfs.Add(new FileSpec(new LocalPath(rec.Filepath), null));
index++;
if (index > MaxFilesIteration)
{
GetFileMetaDataCmdOptions opts = new GetFileMetaDataCmdOptions(GetFileMetadataCmdFlags.AllRevisions, null, null, 0, null, null, null);
worker.ReportProgress(0, m_Repository.GetFileMetaData(lfs, null));
lfs.Clear();
index = 0;
}
}
args.Result = m_Repository.GetFileMetaData(lfs, null); //pass the remaining results across
}
};
worker.ProgressChanged += (sender, args) => PropegateMetaData(args.UserState as IList<FileMetaData>);
worker.RunWorkerCompleted += (sender, args) => PropegateMetaData(args.Result as IList<FileMetaData>);
worker.RunWorkerAsync();
}
private static void PropegateMetaData(IList<FileMetaData> fileList)
{
IList<FileMetaData> fileState = fileList as IList<FileMetaData>;
if (fileState != null)
{
foreach (FileMetaData fmd in fileState)
{
DataRecord currentRecord = DatabaseViewModel.Instance.GetRecordByFilepath(fmd.LocalPath.Path);
if (currentRecord != null)
{
switch (fmd.Action)
{
case FileAction.Add:
currentRecord.P4Status = P4FileState.Added;
break;
case FileAction.Edit:
currentRecord.P4Status = P4FileState.Edit;
break;
case FileAction.MoveAdd:
currentRecord.P4Status = P4FileState.MoveAdd;
break;
default:
currentRecord.P4Status = P4FileState.None;
break;
}
}
}
}
}
What I am trying to achieve is to add text after every operation to a RichTextBox.
The problem is, that these operations take some time and instead of viewing the appended text after every operation finishes, I view them all at the end of the routine.
Semi-Pseudo code:
RichTextBox richTextBox = new RichTextBox()
if (Operation1())
{
richTextBox.AppendText("Operation1 finished");
if (Operation2())
{
richTextBox.AppendText("Operation2 finished");
if (Operation3())
{
richTextBox.AppendText("Operation3 finished");
}
}
}
The problem is that I view the appended text of operation 1 & 2 after the operation 3 is finished.
I read somewhere that I need to use something called BackgroundWorker???
Using BackgroundWorker, you would just put the background work into DoWork, and the update into RunWorkerCompleted:
var bw1 = new BackgroundWorker();
var bw2 = new BackgroundWorker();
var bw3 = new BackgroundWorker();
bw1.DoWork += (sender, args) => args.Result = Operation1();
bw2.DoWork += (sender, args) => args.Result = Operation2();
bw3.DoWork += (sender, args) => args.Result = Operation2();
bw1.RunWorkerCompleted += (sender, args) => {
if ((bool)args.Result)
{
richTextBox.AppendText("Operation1 ended\n");
bw2.RunWorkerAsync();
}
};
bw2.RunWorkerCompleted += (sender, args) => {
if ((bool)args.Result)
{
richTextBox.AppendText("Operation2 ended\n");
bw3.RunWorkerAsync();
}
};
bw3.RunWorkerCompleted += (sender, args) => {
if ((bool)args.Result)
{
richTextBox.AppendText("Operation3 ended\n");
}
};
bw1.RunWorkerAsync();
You'll notice that this runs afoul of "DRY". You could always consider abstracting the tasks for each step using something like:
var operations = new Func<bool>[] { Operation1, Operation2, Operation3, };
var workers = new BackgroundWorker[operations.Length];
for (int i = 0; i < operations.Length; i++)
{
int locali = i; // avoid modified closure
var bw = new BackgroundWorker();
bw.DoWork += (sender, args) => args.Result = operations[locali]();
bw.RunWorkerCompleted += (sender, args) =>
{
txt.Text = string.Format("Operation{0} ended\n", locali+1);
if (locali < operations.Length - 1)
workers[locali + 1].RunWorkerAsync();
};
workers[locali] = bw;
}
workers[0].RunWorkerAsync();
You could do the above 3 times, or use ReportProgress to run all tasks in one background thread, and periodically report progress.
The way that WPF (and most other UI frameworks work) is that there is a UI thread, which handles all the UI events (such as button clicking) and UI drawing.
The UI can't draw things if it's busy doing other things. What's happening is this:
You click a button
The UI thread gets a button click message, and invokes your click handler function
Now, the UI can't redraw or perform any other updates until your click handler function finishes.
Your Operation1 function finishes, and you append to the RichTextBox
The UI can't update because it's still stuck running your code
Your Operation2 function finishes, and you append to the RichTextBox
The UI can't update because it's still stuck running your code
Your Operation3 function finishes, and you append to the RichTextBox
Your function finishes, and now the UI thread is free, and it can finally process the updates and redraw itself.
This is why you see a pause and then all 3 updates together.
What you need to do is make the code that takes a long time run on a different thread so that the UI thread can remain free to redraw and update when you'd like it to. This sample program works for me - it requires .NET 4.5 to compile and run
using System.Threading.Tasks;
...
// note we need to declare the method async as well
public async void Button1_Click(object sender, EventArgs args)
{
if (await Task.Run(new Func<bool>(Operation1)))
{
richTextBox.AppendText("Operation1 finished");
if (await Task.Run(new Func<bool>(Operation2)))
{
richTextBox.AppendText("Operation2 finished");
if (await Task.Run(new Func<bool>(Operation3)))
{
richTextBox.AppendText("Operation3 finished");
}
}
}
}
What happens here is that we use the C# magical async feature, and the order of operations goes like this:
You click a button
The UI thread gets a button click message, and invokes your click handler function
Instead of calling Operation1 directly, we pass it to Task.Run. This helper function will run your Operation1 method on a thread pool thread.
We use the magic await keyword to wait for the thread pool to finish executing operation1. What this does behind the scenes is something morally equivalent to this:
suspend the current function - and thus free up the UI thread to re-draw itself
resume when the thing we're waiting for completes
Because we're running the long operations in the thread pool now, the UI thread can draw it's updates when it wants to, and you'll see the messages get added as you'd expect.
There are some potential drawbacks to this though:
Because your Operation1 method is Not running on the UI thread, if it needs to access any UI related data (for example, if it wants to read some text from a textbox, etc), it can no longer do this. You have to do all the UI stuff first, and pass it as a parameter to the Operation1 method
It's generally not a good idea to put things that take a long time (more than say 100ms) into the thread pool, as the thread pool can be used for other things (like network operations, etc) and often needs to have some free capacity for this. If your app is just a simple GUI app though, this is unlikely to affect you.
If it is a problem for you, you can use the await Task.Factory.StartNew<bool>(_ => Operation1(), null, TaskCreationOptions.LongRunning))) instead and each task will run in it's own thread and not use the thread pool any more. It's a bit uglier though :-)
Basically, this is what happens. I have a thread(endless loop) that runs as a background process while the form is showing. The thread checks if there is a need to add a new ToolStripMenuItem.
If the conditions are met, I'll need to use Invoke in order to create the UI object right? Problem with this is, when the this.Invoke or BeginInvoke is called, the form became unresponsive while the thread that does the checking is still running fine. Any ideas?
This is the first time i'm trying with this multithreading thingee. I'm sure i've missed out something.
public void ThreadSetCom()
{
while (true)
{
string[] tmpStrPort = System.IO.Ports.SerialPort.GetPortNames();
IEnumerable<string> diff = tmpStrPort.Except(strPort);
strPort = tmpStrPort;
System.Console.WriteLine(System.IO.Ports.SerialPort.GetPortNames().Length);
foreach (string p in diff)
{
var cpDropdown = (ToolStripMenuItem)msMenu.Items["connectToolStripMenuItem"];
cpDropdown = (ToolStripMenuItem)cpDropdown.DropDownItems["connectReaderToolStripMenuItem"];
ToolStripMenuItem tsmi = new ToolStripMenuItem();
tsmi.Text = p;
tsmi.Name = p;
tsmi.Click += new EventHandler(itm_Click);
if (this.msMenu.InvokeRequired)
{
GUIUpdate d = new GUIUpdate(ThreadSetCom);
this.Invoke(d);
}
else
{
cpDropdownList.DropDownItems.Add(tsmi);
}
}
}
}
Your ThreadSetCom method never exits:
while (true)
... with no return or break statements. That's going to hang the UI thread forever.
It's not clear what you're trying to achieve, but you definitely don't want to be looping like that in the UI thread. I'd argue that you don't want to be looping like that in a tight way in any thread, mind you...
I think a better approach for you would probably be to use a BackgroundWorker. I say that because what you're experiencing isn't that uncommon when doing multi-threading in a Windows Forms application. Further, the BackgroundWorker is able to manage the thread switching properly. Let me give you an example of that code with the BackgroundWorker.
Build a private class variable
private BackgroundWorker _worker;
Add to the CTOR
public {ctor}()
{
_worker = new BackgroundWorker();
_worker.WorkerSupportsCancellation = true;
_worker.WorkerReportsProgress = true;
_worker.DoWork += new DoWorkEventHandler(BackgroundThreadWork);
_worker.ProgressChanged += new ProgressChangedEventHandler(BackgroundThreadProgress);
}
DoWork handler
private void BackgroundThreadWork(object sender, DoWorkEventArgs e)
{
while (!_worker.CancellationPending)
{
string[] tmpStrPort = System.IO.Ports.SerialPort.GetPortNames();
IEnumerable<string> diff = tmpStrPort.Except(strPort);
strPort = tmpStrPort;
System.Console.WriteLine(System.IO.Ports.SerialPort.GetPortNames().Length);
foreach (string p in diff)
{
_worker.ReportProgress(1, p);
}
}
}
Report progress handler
private void BackgroundThreadProgress(object sender, ReportProgressEventArgs e)
{
var cpDropdown = (ToolStripMenuItem)msMenu.Items["connectToolStripMenuItem"];
cpDropdown = (ToolStripMenuItem)cpDropdown.DropDownItems["connectReaderToolStripMenuItem"];
ToolStripMenuItem tsmi = new ToolStripMenuItem();
tsmi.Text = e.UserState as string;
tsmi.Name = e.UserState as string;
tsmi.Click += new EventHandler(itm_Click);
cpDropdownList.DropDownItems.Add(tsmi);
}
The Loop
However, one thing you're going to have to do is figure out how to get out of this loop. When should it exit? Whatever that means, you need to add to the if statement that exists there in my example because this loop will never end otherwise.
What the effect of this code snippet:
GUIUpdate d = new GUIUpdate(ThreadSetCom);
this.Invoke(d);
is that the method 'ThreadSetCom' will be invoked in the UI thread. And there is an infinitive loop in that method. That is why your form becomes unresponsive.
I suggest you that you should move the foreach clause to a separate method and invoke this method in the UI thread when the condition is hit, for example the diff.Count>0.
I am trying to figure out the best way to keep my application responsive. Below shows the code that I am currently working with. What i have found is that the Background worker thread is the way to go.
private void cleanFiles()
{
if (listView1.CheckedItems.Count != 0)
{
// If so, loop through all checked files and delete.
foreach (ListViewItem item in listView1.CheckedItems)
{
string fileName = item.Text;
string filePath = Path.Combine(tFile + fileName);
try
{
File.Delete(filePath);
}
catch (Exception)
{
//ignore files being in use
}
MessageBox.Show("Files Cleaned");
}
}
else
{
MessageBox.Show("Please put a check by the files you want to delete");
}
}
}
}
The easiest way to keep your program responsive is to use the BackgroundWorker.
List<string listWithFilenames = new List<string>();
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync(listWithFilenames);
See the documentation.
Have a look at these questions:
Cross-Threading issue with listView
How to execute code in the GUI Thread
assuming that the method you posted runs under the context of the UI thread,
all you need to do is wrap the logic (the foreach part)
in a method like :
private void DeleteFiles(object state)
{
/// your logic here
}
and call the ThreadPool.QueueWorkItem(new WaitCallback(DeleteFiles));
from the cleanfiles method.
if you run .NET 4.0, you can use something like:
Task myTask = Task.Factory.StartNew( () => DoWork(null));
then check the myTask status later , to see if its done.
I am trying to call a method which will return me Boolean values(-1,0,1) by invoking a thread something like:
public void CreateThread()
{
ThreadStart ts =delegate{check(55);};
Thread tUpdateNow = new Thread(ts);
tUpdateNow.Start();
//According to the return value perform some task.
}
public bool check(int n)
{
if(n%2==0)
return 1;
else if(n%2>=0)
return 0;
else
return -1;
}
But I don't know how to implement this using C#. Anybody help me please.
You can use the Task Parallel Library which wraps all this for you.
Task<bool> task = Task<bool>.Factory.StartNew(() => check(55));
bool result = task.Result;
I highly recommend working with BackgroundWorker Class for something like this. You can subscribe to events that will fire when the thread completes. See: BackgroundWorker Events.
Something like:
var bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
You can store the result of the DoWork method in the DoWorkEventArgs Result property. When the RunWorkerCompleted event fires, you can retrieve this result from the RunWorkerCompletedEventArgs Result property.
ThreadStart returns void, so in your delegate you are calling your check method and it is throwing away the return value. You will need to store that value somewhere else and then check it once the thread has completed.