I am trying to cancel a background worker if its currently running, and then start another.
I tried this first, there are more checks for cancel in the functions...
private void StartWorker()
{
if (StartServerGetIP.IsBusy) { StartServerGetIP.CancelAsync(); }
StartServerGetIP.RunWorkerAsync();
}
private void StartServerGetIP_DoWork(object sender, DoWorkEventArgs e)
{
StartFTPServer(Port, Ringbuf, sender as BackgroundWorker, e);
if ((sender as BackgroundWorker).CancellationPending) return;
GetIP(Ringbuf, sender as BackgroundWorker, e);
}
private void StartServerGetIP_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
return;
}
if (e.Result.ToString() == "Faulted")
{
tcs.SetResult(false);
return;
}
Client.IPAddress = e.Result.ToString();
tcs.SetResult(true);
}
This approach blocks if the worker is canceled on StartServerGetIP.RunWorkerAsync();
After this I found an ugly solution in
private void StartWorker()
{
if (StartServerGetIP.IsBusy) { StartServerGetIP.CancelAsync(); }
while(StartServerGetIP.IsBusy) { Application.DoEvents(); }
StartServerGetIP.RunWorkerAsync();
}
Is there a pattern I can implement that will allow me to async cancel the background worker and start another without calling Application.DoEvents?
EDIT: A cancel button is out of the question.
EDIT: For those asking about the inner methods...
private void StartFTPServer(SerialPort port, RingBuffer<string> buffer, BackgroundWorker sender, DoWorkEventArgs args)
{
Stopwatch timeout = new Stopwatch();
TimeSpan max = TimeSpan.FromSeconds(MaxTime_StartServer);
int time_before = 0;
timeout.Start();
while (!buffer.Return.Contains("Run into Binary Data Comm mode...") && timeout.Elapsed.Seconds < max.Seconds)
{
if (timeout.Elapsed.Seconds > time_before)
{
time_before = timeout.Elapsed.Seconds;
sender.ReportProgress(CalcPercentage(max.Seconds, timeout.Elapsed.Seconds));
}
if (sender.CancellationPending)
{
args.Cancel = true;
return;
}
}
port.Write("q"); //gets into menu
port.Write("F"); //starts FTP server
}
private void GetIP(RingBuffer<string> buffer, BackgroundWorker sender, DoWorkEventArgs args)
{
//if longer than 5 seconds, cancel this step
Stopwatch timeout = new Stopwatch();
TimeSpan max = TimeSpan.FromSeconds(MaxTime_GetIP);
timeout.Start();
int time_before = 0;
string message;
while (!(message = buffer.Return).Contains("Board IP:"))
{
if (timeout.Elapsed.Seconds > time_before)
{
time_before = timeout.Elapsed.Seconds;
sender.ReportProgress(CalcPercentage(max.Seconds, timeout.Elapsed.Seconds + MaxTime_StartServer));
}
if (timeout.Elapsed.Seconds >= max.Seconds)
{
args.Result = "Faulted";
return;
}
if (sender.CancellationPending)
{
args.Cancel = true;
return;
}
}
Regex regex = new Regex(#"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b");
string IP = message.Remove(0, "Board IP: ".Length);
if (regex.IsMatch(IP))
{
args.Result = IP;
ServerAlive = true;
}
}
Might as well give you the ring buffer too..
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FPGAProgrammerLib
{
class RingBuffer<T>
{
T [] buffer { get; set; }
int _index;
int index
{
get
{
return _index;
}
set
{
_index = (value) % buffer.Length;
}
}
public T Add
{
set
{
buffer[index++] = value;
}
}
public T Return
{
get
{
return (index == 0) ? (IsString() ? (T)(object)string.Empty : default(T)) : buffer[--index];
}
}
private bool IsString()
{
return (typeof(T) == typeof(string) || (typeof(T) == typeof(String)));
}
public RingBuffer(int size)
{
buffer = new T[size];
index = 0;
}
}
}
In your StartServerGetIP_DoWork method there's a StartFTPServer method. I assume you don't check in that method if a cancellation has been requested. The same thing applies to your GetIP method. Those are probably your blocking points. If you want to ensure to actually cancel the job, you need to check periodically if a cancellation has been requested. So I would suggest you use an async method for StartFTPServer and GetIP that will check if the background worker has a cancellation requested.
I don't know the exact implementation you did in the StartFTPServer method or the GetIP method. If you would like more details on how to refactor the code so it can be cancelled post the code.
Here's a simple way to effectively cancel an in-flight function that's operating on another thread by using Microsoft's Reactive Framework (Rx).
Start with a long-running function that returns the value you want:
Func<string> GetIP = () => ...;
And you have some sort of trigger - could be a button click or a timer, etc - or in my case I'm using a type from the Rx library.
Subject<Unit> trigger = new Subject<Unit>();
Then you can write this code:
IObservable<string> query =
trigger
.Select(_ => Observable.Start(() => GetIP()))
.Switch()
.ObserveOn(this);
IDisposable subscription =
query
.Subscribe(ip => { /* Do something with `ip` */ });
Now anytime that I want to initiate the function I can call this:
trigger.OnNext(Unit.Default);
If I initiate a new call while an existing call is running the existing call will be ignored and only the latest call (so long as it completes) will end up being produced by the query and the subscription will get it.
The query keeps running and responds to every trigger event.
The .ObserveOn(this) (assuming you're using WinForms) brings the result back on to the UI thread.
Just NuGet "System.Reactive.Windows.Forms" to get the bits.
If you want trigger to be a button click, do this:
IObservable<Unit> trigger =
Observable
.FromEventPattern<EventHandler, EventArgs>(
h => button.Click += h,
h => button.Click -= h)
.Select(_ => Unit.Default);
Related
I'm working on a basic audio player and I want to update some GUI elements based on the progression through the song.
Next to my Form I use an AudioPlayer class, which contains a ref on the created Form.
In the playAudio function I want to start a timer, which should call updateCurrTime, when elapsed. (For reference: I'm using NAudio)
The function calling the timer:
public bool playAudio()
{
if (waveOutDevice.PlaybackState == PlaybackState.Playing)
{
waveOutDevice.Pause();
timer.Enabled = false;
return false;
}
else if(waveOutDevice.PlaybackState == PlaybackState.Paused)
{
waveOutDevice.Play();
timer.Enabled = true;
return true;
}
else if(waveOutDevice.PlaybackState == PlaybackState.Stopped)
{
initPlayer(mu_path);
waveOutDevice.Play();
timer.Enabled = true;
return true;
}
return false;
}
And the function to update my Form with:
public void updateCurrTime()
{
while (waveOutDevice.PlaybackState == PlaybackState.Playing)
{
form1_ref.curr_time = (int)audioFileReader.CurrentTime.TotalSeconds;
}
}
I defined the timer like this:
timer = new Timer();
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
timer.Interval = 100;
}
and the OnTimedEvent like this:
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
self_ref.updateCurrTime();
}
I use a getter/setter structure for the label text:
public int curr_time
{
get { return Convert.ToInt32(this.l_t_curr.Text); }
set { this.l_t_curr.Text = value.ToString() + "s"; }
}
My problem is, that I'm getting an error, because the form is created on another thread. I did my research, but tbh, I didn't understand, how to implement BackGroundWorker or other solutions in my case.
With help of Julo's hint I was able to fix the issue.
public void updateCurrTime()
{
MethodInvoker methodInvokerDelegate = delegate ()
{ form1_ref.l_t_curr.Text = audioFileReader.CurrentTime.TotalSeconds.ToString(); };
//form1_ref.curr_time = (int)audioFileReader.CurrentTime.TotalSeconds;
//This will be true if Current thread is not UI thread.
if (form1_ref.InvokeRequired)
form1_ref.Invoke(methodInvokerDelegate);
else
methodInvokerDelegate();
}
To update GUI from another thread, you need to use Invoke or BeginInvoke.
Example:
private void GuiUpdate(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.BeginInvoke((MethodInvoker)delegate
{
GuiUpdate(sender, e);
});
return;
}
// put here GUI updating code
}
Difference between Invoke or BeginInvoke is:
Invoke stops execution of current thread until the called function ends,
when using BeginInvoke the starting thread continues without interruption.
Use Invoke when you need result from the function, or priority update. Otherwise it is better to use BeginInvoke.
It is know that Invoke method is used when u need to update gui from other thread. But How can I implement this without binding control to code?
Here's my test class:
class test
{
public List<Thread> threads = new List<Thread>();
public int nThreads = 0;
public int maxThreads = 5;
public void DoWork(object data)
{
string message = (string)data;
//MessageBox.Show(message);
}
public void CreateThread(object data)
{
if (nThreads >= maxThreads)
return;
Thread newThread = new Thread(DoWork);
threads.Add(newThread);
newThread.IsBackground = true;
newThread.Start(data);
nThreads++;
}
public void WindUpThreads()
{
//MessageBox.Show("count: " + nThreads.ToString());
for(int i = 0; i < threads.Count; i++)
{
if (threads[i].IsAlive == false)
{
threads[i].Abort();
threads.RemoveAt(i);
//MessageBox.Show("removing at " + i.ToString());
}
}
nThreads = threads.Count;
}
}
The question is = what tecnique I must use in order to update gui but not hardcode control into class? I've tried to pass delegate to DoWork Method, but this doesn't work (http://pastebin.com/VaSYFxPw). Thanks!
I'm using WinForms, .NET 3.5
Here's the button_click handler:
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
test thTest = new test();
string[] strings;
try
{
strings = File.ReadAllLines("C:\\users\\alex\\desktop\\test.txt");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
bool flag = true;
int counter = 0;
int dataCount = strings.Length;
while (flag == true)
{
if (counter >= dataCount)
{
flag = false;
}
while (thTest.nThreads < thTest.maxThreads)
{
if (flag == false)
break;
thTest.CreateThread(strings[counter]);
//Data d = new Data();
//d.deleg = AddItem;
//d.mess = strings[counter];
//thTest.CreateThread((object)d);
//MessageBox.Show(counter.ToString());
counter++;
}
thTest.WindUpThreads();
if (flag == false)
{
do
{
thTest.WindUpThreads();
} while (thTest.nThreads != 0);
}
}
listBox1.Items.Add("Done");
}
The idea is that I'am launching threads for each task I want to process. After while I'am checking are there completed tasks, then they being shutdowned and new ones are launched until there no more tasks left.
Rather than making DoWork responsible for updating the UI with the results of the operation it performs, simply have it return the value:
//TODO change the type of the result as appropriate
public string DoWork(string message)
{
string output = "output";
//TODO do some work to come up with the result;
return output;
}
Then use Task.Run to create a Task that represents that work being done in a thread pool thread. You can then await that task from your button click handler.
private async void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
test thTest = new test();
//I'd note that you really should pull out reading in this file from your UI code;
//it should be in a separate method, and it should also be reading
//the file asynchronously.
string[] strings;
try
{
strings = System.IO.File.ReadAllLines("C:\\users\\alex\\desktop\\test.txt");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
foreach (var line in strings)
{
var result = await thTest.DoWork(line);
listBox1.Items.Add(result);
}
listBox1.Items.Add("Done");
}
If you really want to be old school about it, you can use a BackgroundWorker instead. Simply do your work in the DoWork handler, setting the result (through the argument) when you've computed it, and update the UI with the result in the RunWorkerCompleted event handler. This lets you keep the UI and non-UI work separate, although it's far less powerful, general purpose, and extensible, as the newer features.
The question is = what tecnique I must use in order to update gui but not hardcode control into class? I've tried to pass delegate to DoWork Method, but this doesn't work
This is indeed the one of the possible techniques. It doesn't work because you have a blocking loop in the UI thread - the most of the code inside the button1_Click handler. It doesn't matter that you spawn additional worker threads - that code keeps the UI thread busy, thus Control.Invoke / Control.BeginInvoke doesn't work because they are processed by the UI thread message loop, which in this case has no chance to do that. The end result is a classical deadlock.
So, you can use the delegate approach, but to make it work, you need to move that code in a separate thread. Something like this
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
var worker = new Thread(DoWork);
worker.IsBackground = true;
worker.Start();
}
private void OnWorkComplete(Exception error)
{
if (error != null)
MessageBox.Show(error.Message);
button1.Enabled = true;
}
private void DoWork()
{
Exception error = null;
try { DoWorkCore(); }
catch (Exception ex) { error = ex; }
Invoke(new Action(OnWorkComplete), error);
}
private void DoWorkCore()
{
test thTest = new test();
// NOTE: No try/catch for showing message boxes, this is running on a non UI thread
string[] strings = File.ReadAllLines("C:\\users\\alex\\desktop\\test.txt");
bool flag = true;
int counter = 0;
int dataCount = strings.Length;
// The rest of the code...
// Pass a delegate to the other threads.
// Make sure using Invoke when you need to access/update UI elements
}
I am trying to move as much processing out of the UI thread on my Windows Phone app. I have some code that is being executed when I click on a button. The code is conceptually similar to the code below.
private int Processing(int a, int b, int c) {
this.A = this.moreProcessing(a);
this.B = this.moreProcessing(b);
this.C = this.moreProcessing(c);
int newInt = /* ... */
return newInt;
}
public void Button_Click(object sender, EventArgs args) {
var result = Processing(1, 2, 3);
this.MyTextBox.Content = result;
}
That would be very easy to move the execution on that code on a thread if the Processing method wasn't setting/getting global state variables.
How do I make sure that only one thread at a time is running in the right sequence? Right now it is easy since the processing code runs on the UI thread. The nice thing about the UI thread is that it guarantee me that everything runs in the right order and one at a time. How do I replicate that with threads?
I could refactor the entire code to have almost no global state, but cannot necessarily do that right now. I could also use lock, but I am just wondering if there's a better way. The processing I am doing isn't super heavy. However, I sometime see some lag in the UI and I want to keep the UI thread as free as possible.
Thanks!
There are a few approaches.
If you intend to fire up a new thread for every Button_Click event, then indeed you could have multiple threads that wish to write to the same variables. You can solve that by wrapping the access to those variables in a lock statement.
Alternatively, you could have one thread always running dedicated to the Processing thread. Use a BlockingCollection to communicate between the UI thread and the Processing thread. Whenever a Button_Click happens, place the relevant info on the BlockingCollection, and have the Processing thread pull work items off of that BlockingCollection.
Untested code that should be close to OK:
class ProcessingParams // Or use a Tuple<int, int, int>
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
}
BlockingCollection<int> bc = new BlockingCollection<int>();
private int Processing() {
try
{
while (true)
{
ProcesingParams params = bc.Take();
this.A = this.moreProcessing(params.A);
this.B = this.moreProcessing(params.B);
this.C = this.moreProcessing(params.C);
int newInt = /* ... */
return newInt; // Rather than 'return' the int, place it in this.MyTextBox.Content using thread marshalling
}
}
catch (InvalidOperationException)
{
// IOE means that Take() was called on a completed collection
}
}
public void Button_Click(object sender, EventArgs args) {
//var result = Processing(1, 2, 3);
bc.Add (new ProcessingParams() { A = 1, B = 2, C = 3 };
//this.MyTextBox.Content = result;
}
When your application closes down, remember to call
bc.CompleteAdding(); // Causes the processing thread to end
A very simple solution is to use a BackgroundWorker. It allows you to offload your work to a background thread and notify you when it is complete. (see below for another option)
void Button_Click(object sender, EventArgs args)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (s, e) =>
{
e.Result = Processing(1, 2, 3);
};
worker.RunWorkerCompleted += (s1, e1) =>
{
MyTextBox.Content = e1.Result;
MyButton.IsEnabled = true;
};
// Disable the button to stop multiple clicks
MyButton.IsEnabled = false;
worker.RunWorkerAsync();
}
Another option is to get your code ready for the next version of Windows Phone and start using the Task Parallel Library. TPL is available with .Net4, but is not available with Windows Phone. There are some NuGet packages that do support Silverlight and Windows Phone. Add one of these packages to your project and you can change your code to (syntax may not be 100% correct):
private Task<int> ProcessAsync(int a, int b, int c)
{
var taskCompletionSource = new TaskCompletionSource<int>();
var task = Task.Factory.StartNew<int>(() =>
{
// Do your work
return newInt;
}
task.ContinueWith(t => taskCompletionSource.SetResult(t.Result));
return taskCompletionSource.Task;
}
void Button_Click(object sender, EventArgs args)
{
// Disable the button to prevent more clicks
MyButton.IsEnabled = false;
var task = ProcessAsync(1,2,3);
task.ContinueWith(t =>
{
MyTextBox.Content = t.Result;
MyButton.IsEnabled = true;
});
}
Try this:
public void Button_Click(object sender, EventArgs args)
{
Button.Enabled = false;
ThreadPool.QueueUserWorkItem(new WaitCallback(BackgroundProcessing));
}
private void BackgroundProcessing(object state)
{
var result = Processing(1, 2, 3);
// Call back to UI thread with results
Invoke(new Action(() => {
this.MyTextBox.Content = result;
Button.Enabled = true;
}));
}
private int Processing(int a, int b, int c)
{
this.A = this.moreProcessing(a);
this.B = this.moreProcessing(b);
this.C = this.moreProcessing(c);
int newInt = /* ... */
return newInt;
}
I have 3 background workers each processing a channel of a 24-bit Bitmap image (Y, Cb, Cr). The processing for each 8-bit image takes several seconds and they might not complete at the same time.
I want to merge the channels back into one image when I'm done. When a button is clicked, each of the backgroundWorkerN.RunWorkerAsync() is started and when they complete I set a flag for true. I tried using a while loop while (!y && !cb && !cr) { } to continually check the flags until they are true then exit loop and continue processing the code below which is the code to merge the channels back together. But instead the process never ends when I run it.
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
backgroundWorker2.RunWorkerAsync();
backgroundWorker3.RunWorkerAsync();
while (!y && !cb && !cr) { }
//Merge Code
}
Building on the answer from Renuiz, I would do it this way:
private object lockObj;
private void backgroundWorkerN_RunWorkerCompleted(
object sender,
RunWorkerCompletedEventArgs e)
{
lock (lockObj)
{
y = true;
if (cb && cr) // if cb and cr flags are true -
// other backgroundWorkers finished work
{
someMethodToDoOtherStuff();
}
}
}
Maybe you could set and check flags in background worker complete event handlers. For example:
private void backgroundWorkerN_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
y = true;
if(cb && cr)//if cb and cr flags are true - other backgroundWorkers finished work
someMethodToDoOtherStuff();
}
I would use three threads instead of background workers.
using System.Threading;
class MyConversionClass
{
public YCBCR Input;
public RGB Output
private Thread Thread1;
private Thread Thread2;
private Thread Thread3;
private int pCompletionCount;
public MyConversionClass(YCBCR myInput, RGB myOutput)
{
this.Input = myInput;
this.Output = myOutput;
this.Thread1 = new Thread(this.ComputeY);
this.Thread2 = new Thread(this.ComputeCB);
this.Thread3 = new Thread(this.ComputeCR);
}
public void Start()
{
this.Thread1.Start();
this.Thread2.Start();
this.Thread3.Start();
}
public void WaitCompletion()
{
this.Thread1.Join();
this.Thread2.Join();
this.Thread3.Join();
}
// Call this method in background worker 1
private void ComputeY()
{
// for each pixel do My stuff
...
if (Interlocked.Increment(ref this.CompletionCount) == 3)
this.MergeTogether();
}
// Call this method in background worker 2
private void ComputeCB()
{
// for each pixel do My stuff
...
if (Interlocked.Increment(ref this.CompletionCount) == 3)
this.MergeTogether();
}
// Call this method in background worker 3
private void ComputeCR()
{
// for each pixel do My stuff
...
if (Interlocked.Increment(ref this.CompletionCount) == 3)
this.MergeTogether();
}
private void MergeTogether()
{
// We merge the three channels together
...
}
}
Now in your code you simply do this:
private void button1_Click(object sender, EventArgs e)
{
MyConversionClass conversion = new MyConversionClass(myinput, myoutput);
conversion.Start();
conversion.WaitCompletion();
... your other stuff
}
However this will pause your GUI until all operations are completed.
I would use SynchronizationContext instead to notify the GUI that the operation has completed.
This version uses SynchronizationContext for synchronizing the GUI thread without waiting at all.
This will keep the GUI responsive and performs the entire conversion operation in the other threads.
using System.Threading;
class MyConversionClass
{
public YCBCR Input;
public RGB Output
private EventHandler Completed;
private Thread Thread1;
private Thread Thread2;
private Thread Thread3;
private SynchronizationContext SyncContext;
private volatile int pCompletionCount;
public MyConversionClass()
{
this.Thread1 = new Thread(this.ComputeY);
this.Thread2 = new Thread(this.ComputeCB);
this.Thread3 = new Thread(this.ComputeCR);
}
public void Start(YCBCR myInput, RGB myOutput, SynchronizationContext syncContext, EventHandler completed)
{
this.SyncContext = syncContext;
this.Completed = completed;
this.Input = myInput;
this.Output = myOutput;
this.Thread1.Start();
this.Thread2.Start();
this.Thread3.Start();
}
// Call this method in background worker 1
private void ComputeY()
{
... // for each pixel do My stuff
if (Interlocked.Increment(ref this.CompletionCount) == 3)
this.MergeTogether();
}
// Call this method in background worker 2
private void ComputeCB()
{
... // for each pixel do My stuff
if (Interlocked.Increment(ref this.CompletionCount) == 3)
this.MergeTogether();
}
// Call this method in background worker 3
private void ComputeCR()
{
... // for each pixel do My stuff
if (Interlocked.Increment(ref this.CompletionCount) == 3)
this.MergeTogether();
}
private void MergeTogether()
{
... // We merge the three channels together
// We finish everything, we can notify the application that everything is completed.
this.syncContext.Post(RaiseCompleted, this);
}
private static void RaiseCompleted(object state)
{
(state as MyConversionClass).OnCompleted(EventArgs.Empty);
}
// This function is called in GUI thread when everything completes.
protected virtual void OnCompleted(EventArgs e)
{
EventHandler completed = this.Completed;
this.Completed = null;
if (completed != null)
completed(this, e);
}
}
Now, in your code...
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
MyConversionClass conversion = new MyConversionClass();
conversion.Start(myinput, myoutput, SynchronizationContext.Current, this.conversion_Completed);
}
private void conversion_Completed(object sender, EventArgs e)
{
var output = (sender as MyConversionClass).Output;
... your other stuff that uses output
button1.Enabled = true;
}
The good things of both method is that they are GUI agnostic, you can put them in a library and keep your precious multi-threading conversion code totally independant on the GUI you are using, that is, WPF, Web or Windows Forms.
You can use WaitHandle.WaitAll in conjunction with EventWaitHandle to achieve what you need. Herein enclosed a code sample which does what I mentioned. The enclosed code is just an outline of how the solution will look like. You must add proper exception handling and defensive approach to make this code more stable.
using System;
using System.ComponentModel;
using System.Threading;
namespace ConsoleApplication7
{
class Program
{
static void Main(string[] args)
{
BWorkerSyncExample sample = new BWorkerSyncExample();
sample.M();
}
}
class BWorkerSyncExample
{
BackgroundWorker worker1, worker2, worker3;
EventWaitHandle[] waithandles;
public void M()
{
Console.WriteLine("Starting background worker threads");
waithandles = new EventWaitHandle[3];
waithandles[0] = new EventWaitHandle(false, EventResetMode.ManualReset);
waithandles[1] = new EventWaitHandle(false, EventResetMode.ManualReset);
waithandles[2] = new EventWaitHandle(false, EventResetMode.ManualReset);
StartBWorkerOne();
StartBWorkerTwo();
StartBWorkerThree();
//Wait until all background worker complete or timeout elapse
Console.WriteLine("Waiting for workers to complete...");
WaitHandle.WaitAll(waithandles, 10000);
Console.WriteLine("All workers finished their activities");
Console.ReadLine();
}
void StartBWorkerThree()
{
if (worker3 == null)
{
worker3 = new BackgroundWorker();
worker3.DoWork += (sender, args) =>
{
M3();
Console.WriteLine("I am done- Worker Three");
};
worker3.RunWorkerCompleted += (sender, args) =>
{
waithandles[2].Set();
};
}
if (!worker3.IsBusy)
worker3.RunWorkerAsync();
}
void StartBWorkerTwo()
{
if (worker2 == null)
{
worker2 = new BackgroundWorker();
worker2.DoWork += (sender, args) =>
{
M2();
Console.WriteLine("I am done- Worker Two");
};
worker2.RunWorkerCompleted += (sender, args) =>
{
waithandles[1].Set();
};
}
if (!worker2.IsBusy)
worker2.RunWorkerAsync();
}
void StartBWorkerOne()
{
if (worker1 == null)
{
worker1 = new BackgroundWorker();
worker1.DoWork += (sender, args) =>
{
M1();
Console.WriteLine("I am done- Worker One");
};
worker1.RunWorkerCompleted += (sender, args) =>
{
waithandles[0].Set();
};
}
if (!worker1.IsBusy)
worker1.RunWorkerAsync();
}
void M1()
{
//do all your image processing here.
//simulate some intensive activity.
Thread.Sleep(3000);
}
void M2()
{
//do all your image processing here.
//simulate some intensive activity.
Thread.Sleep(1000);
}
void M3()
{
//do all your image processing here.
//simulate some intensive activity.
Thread.Sleep(4000);
}
}
}
Consider using AutoResetEvents:
private void button1_Click(object sender, EventArgs e)
{
var e1 = new System.Threading.AutoResetEvent(false);
var e2 = new System.Threading.AutoResetEvent(false);
var e3 = new System.Threading.AutoResetEvent(false);
backgroundWorker1.RunWorkerAsync(e1);
backgroundWorker2.RunWorkerAsync(e2);
backgroundWorker3.RunWorkerAsync(e3);
// Keep the UI Responsive
ThreadPool.QueueUserWorkItem(x =>
{
// Wait for the background workers
e1.WaitOne();
e2.WaitOne();
e3.WaitOne();
MethodThatNotifiesIamFinished();
});
//Merge Code
}
void BackgroundWorkerMethod(object obj)
{
var evt = obj as AutoResetEvent;
//Do calculations
etv.Set();
}
This way you do not waste cpu time in some loops & using a seperate thread for waiting keeps the UI Responsive.
With reference to the Software Project I am currently working on.
I have the below methods that basically move a canvas with a Timer:
DispatcherTimer dt = new DispatcherTimer(); //global
public void Ahead(int pix)
{
var movx = 0;
var movy = 0;
dt.Interval = TimeSpan.FromMilliseconds(5);
dt.Tick += new EventHandler((object sender, EventArgs e) =>
{
if (movx >= pix || movy >= pix)
{
dt.Stop();
return;
}
Bot.Body.RenderTransform = new TranslateTransform(movx++, movy++);
});
dt.Start();
}
public void TurnLeft(double deg)
{
var currAngle = 0;
dt.Interval = TimeSpan.FromMilliseconds(5);
dt.Tick += new EventHandler(delegate(object sender, EventArgs e)
{
if (currAngle <= (deg - (deg * 2)))
{
dt.Stop();
}
Bot.Body.RenderTransform = new RotateTransform(currAngle--, BodyCenter.X, BodyCenter.Y);
});
dt.Start();
}
Now, from another library, these methods are called like such:
public void run()
{
Ahead(200);
TurnLeft(90);
}
Now of course, I want these animations to happen after another, but what is happening is that the dt.Tick event handler of the DispatchTimer is being overwritten when the second method (in this case, TurnLeft(90)) is invoked and thus, only the second method gets executed as it should.
I need to create some sort of queue that will allow me to push and pop methods to that queue so that dt (the DispatchTimer timer) executes them one by one...in the order they are in the 'queue'
Any way I can go about doing this ? Am I on the right track here, or completely off course?
When you call Invoke() or BeginInvoke() on the Dispatcher, the operation will be queued up and run when the thread associated with the Dispatcher is free. So instead of using the Tick event, use the overload of Dispatcher.Invoke that takes a Timespan.
I have fixed this problem by myself. What I did was create a global Queue of type Delegate and instead of executing the methods directly, I add them to this queue.
Then I would have a separate thread in the constructor that will dequeue methods one by one and executing them:
Queue<TimerDelegate> eventQueue = new Queue<TimerDelegate>();
public Vehicle(IVehicle veh, Canvas arena, Dispatcher battleArenaDispatcher)
{
DispatcherTimer actionTimer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(100) };
actionTimer.Tick += new EventHandler(delegate(object sender, EventArgs e)
{
if (IsActionRunning || eventQueue.Count == 0)
{
return;
}
eventQueue.Dequeue().Invoke(new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(5) });
});
actionTimer.Start();
}
public void TurnRight(double deg)
{
eventQueue.Enqueue((TimerDelegate)delegate(DispatcherTimer dt)
{
IsActionRunning = true;
var currAngle = 0;
dt.Tick += new EventHandler(delegate(object sender, EventArgs e)
{
lock (threadLocker)
{
if (currAngle >= deg)
{
IsActionRunning = false;
dt.Stop();
}
Rotator_Body.Angle++;
currAngle++;
}
});
dt.Start();
});
}