I have a console application that utilizes the BackgroundWorker object and wanted to test my output with a Console.WriteLine(fooBar). However, that application exits as the application executes the Console.WriteLine command.
Here's a watered down version that illustrates what I wanted to do:
protected static void bWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = startId; i <= endId; i++)
{
Console.WriteLine(i.ToString());
Thread.Sleep(1000);
}
}
Any ideas why the application would appear to exit like that?
The application exits because it in fact completes execution and has no more work to do ;-) Once you schedule the background worker and kick off the thread to do it's thing, you have to tell the main thread to stop and wait for one thing or another. A very common way of doing this (Generally used in test/sample code) is to simply issue a Console.ReadKey(); as the very last line of code in your main method. This will cause your application to wait until you press a key before exiting the process.
For your backgroundworker set WorkerReportsProgress to true. Subscribe to ProgressChanged event like this:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (var i = 0; i < 1000; i++)
{
backgroundWorker1.ReportProgress(i);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Console.WriteLine(e.ProgressPercentage);
}
If you need to transfer more than just int from your background thread to UI thread, then you could do something like this:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (var i = 0; i < 1000; i++)
{
var myObjectInstance = new MyObject{ ...};
backgroundWorker1.ReportProgress(null, myObjectInstance);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
var myObjectInstance = (MyObject)e.UserState;
Console.WriteLine(myObjectInstance);
}
I might not be understanding your setup correctly. I'm guessing that you're running the background thread, but the main process is exiting which causes the thread to be stopped before it gets to do anything. Maybe try putting something in your main process that prevents the main thread from exiting like Console.ReadKey();
Related
I try to perform an easy task in an other backgroundthread, so the UI doesn't get blocked, but it still gets blocked. Did I forget anything?
public partial class backgroundWorkerForm : Form
{
public backgroundWorkerForm()
{
InitializeComponent();
}
private void doWorkButton_Click(object sender, EventArgs e)
{
if (backgroundWorker.IsBusy != true)
{
// Start the asynchronous operation.
backgroundWorker.RunWorkerAsync();
}
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//BackgroundWorker worker = sender as BackgroundWorker;
if (textBoxOutput.InvokeRequired)
{
textBoxOutput.Invoke(new MethodInvoker(delegate
{
for (int i = 0; i < 10000; i++)
{
textBoxOutput.AppendText(i + Environment.NewLine);
}
}));
}
}
}
While the textBox gets filled, the UI is blocked:
Your app wants to repeatedly send updates from the background thread to the UI. There is a built-in mechanism for this: the ProgressChanged event for the background worker. A ReportProgress call is triggered in the background, but executes on the UI thread.
I do change one thing, however. Performance can degrade with too many cross-thread calls. So instead of sending an update every iteration, I instead will batch them into 100.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
const int maxIterations = 10000;
var progressLimit = 100;
var staging = new List<int>();
for (int i = 0; i < maxIterations; i++)
{
staging.Add(i);
if (staging.Count % progressLimit == 0)
{
// Only send a COPY of the staging list because we
// may continue to modify staging inside this loop.
// There are many ways to do this. Below is just one way.
backgroundWorker1.ReportProgress(staging.Count, staging.ToArray());
staging.Clear();
}
}
// Flush last bit in staging.
if (staging.Count > 0)
{
// We are done with staging here so we can pass it as is.
backgroundWorker1.ReportProgress(staging.Count, staging);
}
}
// The ProgressChanged event is triggered in the background thread
// but actually executes in the UI thread.
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.ProgressPercentage == 0) return;
// We don't care if an array or a list was passed.
var updatedIndices = e.UserState as IEnumerable<int>;
var sb = new StringBuilder();
foreach (var index in updatedIndices)
{
sb.Append(index.ToString() + Environment.NewLine);
}
textBoxOutput.Text += sb.ToString();
}
EDIT:
This requires you set the background worker's WorkerReportsProgress property to true.
It's not important that you pass a count with the ReportProgress call. I do so just to have something and to quickly check if I can return.
One really should keep in mind about how many events are being invoked and queued up. Your original app had 10,000 cross thread invocations and 10,000 changed text events for textBoxOutput. My example uses 100 cross thread calls since I use a page size of 100. I could still have generated 10,000 changed text events for the textbox, but instead use a StringBuilder object to hold a full page of changes and then update the textbox once for that page. That way the textbox only has 100 update events.
EDIT 2
Whether or not your app needs paging is not the main deal. The biggest take away should be that the background worker really should use ReportProgress when trying to communicate info back to the UI. See this MSDN Link. Of particular note is this:
You must be careful not to manipulate any user-interface objects in
your DoWork event handler. Instead, communicate to the user interface
through the ProgressChanged and RunWorkerCompleted events.
Your invocation code should be outside the loop. Everything in the invoked codeblock, will be executed on the UI thread, thus blocking it.
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 0; i < 10000; i++)
{
// do long-running task
//if (textBoxOutput.InvokeRequired)
//{
textBoxOutput.Invoke(new MethodInvoker(delegate
{
textBoxOutput.AppendText(i + Environment.NewLine);
}));
//}
}
}
an easier way would be to do completely create your output text, and then paste the full output into the TextBox, then you only need one invocation
protected delegate void SetTextDelegate(TextBox tb, string Text);
protected void SetText(TextBox tb, string Text)
{
if (tb.InvokeRequired) {
tb.Invoke(new SetTextDelegate(SetText), tb, Text);
return;
}
tb.Text = Text;
}
and then inside your dowork
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
StringBuilder sb = new StringBuilder();
//BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 0; i < 10000; i++)
{
sb.AppendLine(i.ToString());
}
SetText(textBoxOutput, sb.ToString());
}
I have made a simple gui in C# wpf, (sorry I can't show the GUI because my reputation is below 10)
It's consist of richtextbox and some other controls. Umm... this application will read a file then display the file contents to richtextbox line by line while reading the file using background worker. The function that read the file is like this :
public int parse_persoFile2(string fname, BackgroundWorker worker, DoWorkEventArgs e)
{
if (fname == null) return -1;
System.IO.StreamReader ifs;
ifs = new System.IO.StreamReader(fname);
int max = (int)e.Argument;
int p = 0;
while (ifs.Peek() != -1)
{
string tempData = ifs.ReadLine();
if (tempData.Contains("CMD=5107") || tempData.Contains("CMD=5106") || tempData.Contains("CMD=5102"))
{
//field.AppendText(tempData.Remove(tempData.LastIndexOf('\\')).Remove(0, 4) + "\r\n");
//field.AppendText("--------\r\n");
//System.Threading.Thread.Sleep(500);
string data = tempData.Remove(tempData.LastIndexOf('\\')).Remove(0, 4) + "\r\n";
worker.ReportProgress(p, data);
}
p++;
}
worker.ReportProgress(100);
return 0;
}
As we can see, I'm using backgroundworker in this function to get the string readed from file then send that string to reportprogress in order to be displayed in richtextbox. As a note that persoFile2 function is made from another object in my program... :-)
Then for the rest, I have made the doWork function, worker_progressChanged, and worker_RunWorkerCompleted to make backroundWorker works correctly. Those codes are like this :
private void doWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker wrk = sender as BackgroundWorker;
parser.parse_persoFile2(fileName, wrk, e);
}
private void proggChanged(object sender, ProgressChangedEventArgs e)
{
if(e.UserState != null)
mRTB.AppendText(e.UserState.ToString());
}
private void completed(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Ok....");
}
Umm.... When I run this program, it looks that my richtextbox is not prints the string line by line from the file, but it prints it just once at the end... :-3, .. Nah that's my real problem here. I have read this article http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx, but still have no idea.... :3
If you call the ReportProgress method too rapidly, it's possible that the UI thread will not have a chance to process the "progress" and update appropriately before the BackgroundWorker is hitting it again.
private void doWork(object sender, DoWorkEventArgs e)
{
var wrk = sender as BackgroundWorker;
// obviously you wouldn't really do this :)
while(true)
wrk.ReportProgress(0);
}
To see the effect you're looking expecting, you could set an artificial "pause" in your DoWork event, in order to give the UI time to update appropriately:
private void doWork(object sender, DoWorkEventArgs e)
{
var wrk = sender as BackgroundWorker;
var p = 0;
while(true)
{
wrk.ReportProgress(p++);
Thread.Sleep(100);
}
}
As for your situation, if the code is executing that quickly, you may not actually need to be executing it in a separate thread.
Alternatively, you could update your UI to say "Please wait. Loading...", then do everything you need to do in the BackgroundWorker, and just return the final result back to the UI at the end.
I've looked at some guides and none of them have gotten me all the way there. I've never made a thread, discussed a thread, or seen a thread at the grocery store, so this may be a problem. Currently. I'm trying:
private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
{
ThreadStart HUp = new ThreadStart(dothis);
t = new Thread(HUp);
t.Start();
}
}
public void dothis()
{
if (intHour < 23)
intHour = intHour += intStep;
lblTimerHour.Text = intHour.ToString("00");
}
private void btnHUp_MouseUp(object sender, MouseEventArgs e)
{
t.Abort();
}
}
That gets me InvalidOperationException was unhandled on the
lblTimerHour.Text = intHour.ToString("00");
line. I read what that means and... it might as well be in Mandarin, I kind of get the general concept-ish of what's going wrong, but it's painfully fuzzy. If you asked me the first step in fixing it I'd look at you like a deer in the headlights. We just haven't gotten that far in my class yet.
The problem here is that the label you are trying to update is owned by the main thread (i.e. what the UI runs on), and that means that only that thread can access/update it. So, since you are in a different thread, you need to tell the UI thread to update the label for you.
Something like this would work:
Action updateLabel = () => lblTimerHour.Text = intHour.ToString("00");
lblTimerHour.BeginInvoke(updateLabel);
What this does is tell the lblTimerHour to invoke the action you define above (updateLabel).
See this post: How to update the GUI from another thread in C#?
lblTimerHour.Invoke((MethodInvoker)delegate {
//Do what you need to do with the label
lblTimerHour.Text = intHour.ToString("00");
});
Edit
This should do the trick:
public void dothis()
{
do
{
if (intHour < 23)
intHour = intHour += intStep;
lblTimerHour.Invoke((MethodInvoker)delegate {
//Update the label from the GUI thread
lblTimerHour.Text = intHour.ToString("00");
});
//Pause 1 sec. Won't freeze the gui since it's in another thread
System.Thread.Sleep(1000);
}while(true); //Thread is killed on mouse up
}
Well, let's take a look and see what you already have.
First, I see you did this.
private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
ThreadStart HUp = new ThreadStart(dothis);
t = new Thread(HUp);
t.Start();
}
While this certainly is not the freshest stuff around it will still work. If you wanted some fresher ingredients then you might go with this instead.
private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
Task.Factory.StartNew(dothis);
}
Second, I see this.
public void dothis()
{
if (intHour < 23) intHour = intHour += intStep;
lblTimerHour.Text = intHour.ToString("00");
}
The problem here is that you are attempting to update a UI control from a thread other than the main UI thread. You see UI controls have what is called thread affinity. They can only ever be accessed from the thread that created them. What you have will lead to all kinds of unpredictable problems up to and including tearing a whole in spacetime.
A better option would be to do this.
public void dothis()
{
while (intHour < 23)
{
intHour = intHour += intStep;
lblTimerHour.Invoke((Action)(
() =>
{
lblTimerHour.Text = intHour.ToString("00");
}));
}
}
I assumed that you were missing the loop so I added it. While I cannot say that I personally have a taste for this kind of thing it is much easier to swallow. The real problem here is that the worker thread really does not do a whole lot of useful work. And then to top it off we have to use an awkward marshaling operation to transfer the result back to the UI thread. It is not pretty, but it will work.
And finally that brings me to this.
private void btnHUp_MouseUp(object sender, MouseEventArgs e)
{
t.Abort();
}
You are attempting to abort a thread which is highly inadvisable. The problem is that it yanks control from the thread at unpredictable times. That thread might be in the middle of a write to data structure which would corrupt it. This is actually a pretty bad problem because any data structure in the process of being manipulated from any one of the frames on the call stack could be in an inconsistent state. This includes code you did not write. That is why it is hard to say what you may or may not be corrupting by doing this.
What you need to consider instead is using the cooperative cancellation mechanisms. This includes the use of CancellationTokenSource and CancellationToken. Here is how it might look once we put everything together.
private CancellationTokenSource cts = null;
private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
cts = new CancellationTokenSource();
Task.Factory.StartNew(() => dothis(cts.Token));
}
private void btnHUp_MouseUp(object sender, MouseEventArgs e)
{
cts.Cancel();
}
public void dothis(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
intHour += intStep;
lblTimerHour.Invoke((Action)(
() =>
{
lblTimerHour.Text = intHour.ToString("00");
}));
Thread.Sleep(1000);
}
}
What this does is signal that the worker thread should gracefully shutdown on its own. This gives the worker thread a chance to tidy things up before eventually terminating itself.
If you want to update the UI every X period of time then there are already existing tools for this; a Timer will do exactly what you want, and it will be much more efficient and easier to code than creating a new thread that just spends most of its time napping. Additionally, aborting threads is a very bad sign to see. Avoid it at all costs.
First create the timer and configure it in the constructor:
private System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
private int hour = 0;
private int step = 0;
public Form1()
{
InitializeComponent();
timer.Tick += timer_Tick;
timer.Interval = 1000;
}
Have the Tick event do whatever should be done whenever it ticks.
private void timer_Tick(object sender, EventArgs e)
{
if (hour < 23)
{
hour += step;
lblTimerHour.Text = hour.ToString("00");
}
}
Then just start the timer when you want it to start ticking and stop the timer when you want it to stop:
private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
timer.Start();
}
private void btnHUp_MouseUp(object sender, MouseEventArgs e)
{
timer.Stop();
}
The timer will automatically ensure that the Tick event handler runs in the UI thread, and it won't block the UI thread (or any other thread) when its waiting for the next event to happen, it will just do nothing.
when I click button1 should print A s but when I click button2 need to stop thread1 and need to start thread2, what is the wrong in here please help me
private void button1_Click(object sender, EventArgs e)
{
if (thread2.IsAlive)
{
thread2.Suspend();
}
thread1 = new Thread(threadOne);
thread1.Start();
}
private void button2_Click(object sender, EventArgs e)
{
if (thread1.IsAlive)
{
thread1.Suspend();
}
thread2 = new Thread(threadTwo);
thread2.Start();
}
private void threadOne() {
for (int i=0; i < 20; i++ )
{
Console.Write("A");
Thread.Sleep(500);
}
}
private void threadTwo()
{
for (int i = 0; i < 20; i++)
{
Console.Write("B");
Thread.Sleep(500);
}
}
There's quite a lot wrong, as the other posters have correctly pointed out. I would add this:
1) Most threads in commercial-grade software never terminate during the lifetime of the application - they are written as infinite loops with blocking calls that wait for some sort of signaling from other threads or I/O operations.
2) Continual create/terminate/destroy of thread objects is expensive, difficult to control, awkward-to-debug, unreliable and generally causes pain.
3) If your multithread code contains any of the following:
Suspend()
Resume(), (except in some thread ctors)
Join()
Abort()
IsAlive()
You should probably think again :)
I know you're only learning, but sometimes it's better to patch up holes before they get any bigger :)
You are creating instance of Thread's in the Button Click events, so the if statement that checks threadX.IsAlive will throw error reporting null reference exception
So please create the instance of thread1 & 2 on load
Check for null also.
Use Abort() method instead of Suspend() as you are anyway creating a new thread each time.
private void button1_Click(object sender, EventArgs e)
{
if (thread2!=null && thread2.IsAlive)
{
thread2.Abort();
}
thread1 = new Thread(threadOne);
thread1.Start();
}
private void button2_Click(object sender, EventArgs e)
{
if (thread1!=null && thread1.IsAlive)
{
thread1.Abort();
}
thread2 = new Thread(threadTwo);
thread2.Start();
}
Hi I have a problem using Filesystemwatcher & BackgroundWorker process.
I have a windows forms application that checks for new text files on a folder, it process them and creates xml files from them.
I´m using FSW to monitor for new txt files on a folder, the app works fine but when the folder receives a large amount of files (let's say 1000), the app freezes because it's processing all of them.
it occurred to me to add a backgroundworker, so the FSW calls it everytime a new file is created, this way we can process the file on the background without freezing the UI.
This idea did not work because for every file that is created, I try to call the RunWorkerAsync() method, so if it's busy processing a file and I try to process a new one it will throw the following error:
"This BackgroundWorker is currently busy and cannot run multiple tasks concurrently."
So I tried to loop the method with a while til it gets available but, infinite exception is thrown.
this is the simplified version of my code:
private void fileSystemWatcher1_Created(object sender, System.IO.FileSystemEventArgs e)
{
readFile();
}
private void readFile()
{
while (backgroundWorker1.IsBusy)
{
readFile();
}
backgroundWorker1.RunWorkerAsync(idx);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
int i = (int)e.Argument;
i += 1;
e.Result = i;
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = "Processing...";
this.Refresh();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
label1.Text = "Completed...";
this.Refresh();
idx = (int)e.Result;
}
The exception thrown says “An unhandled exception of type 'System.StackOverflowException' occurred in WindowsFormsApplication2.exe, make sure you do not have an infinite loop or recursion”
Of course I could remove the FSW, but I'd like to know if there's a way to make them work together, any ideas?
What you have is a classic Producer/Consumer problem.
Solve it with a System.Collections.Concurrent.ConcurrentQueue<string>.
On the FSW event, add the filename to the Queue.
Start 1 or 2 BackgroundWorkers to process the queue.
And this is the code that overflows your stack in a hurry:
private void readFile()
{
while (backgroundWorker1.IsBusy)
{
readFile(); // the recursive call, will fail quickly
}
backgroundWorker1.RunWorkerAsync(idx);
}
Not only does this cause an SO exception, it also blocks your main thread.
You need a better way to wait, and the ConcurrentQueue gives you that.
Instantiating new BackgroundWorkers would do the trick, as would Henk's solution above.
Or, you can do it without changing your code too much just using the ThreadPool.
private void fileSystemWatcher1_Created(object sender, System.IO.FileSystemEventArgs e)
{
ThreadPool.QueueUserWorkItem(o => readFile(e));
}
public void readFile(System.IO.FileSystemEventArgs e)
{
this.BeginInvoke(new MethodInvoker(() =>
{
label1.Text = "Processing...";
this.Refresh(); //you shouldn't need this
}));
//your long running read/processing... doing something event args
this.BeginInvoke(new MethodInvoker(() =>
{
label1.Text = "Completed...";
this.Refresh();
idx = (int) e.Result;
}));
}
Why not instantiate a new BackgroundWorker in readFile instead of reusing?