I'm using A background worker for BLE RSSI level test.
my problem is that RunWorkerCompleted event is fired immediately, way before DoWork done it's operation.
most of the DoWork event operation is to create an advertisement watcher and wait for Signal from A Bluetooth low energy device.
the signal level will be updated fromthe main thread and handling of the result will be on the background worker.
here is when I call the Background worker:
...
worker = new BackgroundWorker();
worker.DoWork += callTestBLE;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.RunWorkerAsync(RSSI_Label);
}
The event handlers:
private async void callTestBLE(object sender, DoWorkEventArgs e)
{
BluetoothLEAdvertisementWatcher watcher1 ;
BluetoothLEAdvertisementFilter advertisementFilter1;
int rssiRetries1 = RSSIRETRIES;
RssiValue = "";
advertisementFilter1 = new BluetoothLEAdvertisementFilter();
try
{
advertisementFilter1.Advertisement.LocalName = myUswm.getAdvetrismentName();
checkRSSI = true;
}
catch (Exception) { checkRSSI = false; return; }
watcher1 = new BluetoothLEAdvertisementWatcher(advertisementFilter);
watcher1.ScanningMode = BluetoothLEScanningMode.Active;
watcher1.Received += OnAdvertisementReceived;
// Wait 5 seconds to make sure the device is really out of range
watcher1.SignalStrengthFilter.OutOfRangeTimeout = TimeSpan.FromMilliseconds(5000);
watcher1.SignalStrengthFilter.SamplingInterval = TimeSpan.FromMilliseconds(2000);
try
{
watcher1.Start();
await testBLEAsync();
if (myUswm.getConnectionStatus() == DISCONNECTED)
{
checkNextUUTClick(new object(), new RoutedEventArgs()); return;
}
for (int i = 0; i < 5; i++)
{
// if (RssiSamplesNum <= 0 || --rssiRetries < 0)
if (RssiSamplesNum <= 0 || --rssiRetries1 < 0)
{
//serviceList.Clear();
watcher1.Stop();
rssiRetries1 = RSSIRETRIES;
RssiSamplesNum1 = numOfAdvertismentSamples;
break;
}
else
{
((Label)e.Argument).Content = RssiValue;
/*RSSI_Label.Dispatcher.Invoke(new Action(() =>
{ RSSI_Label.Content = RssiValue; }));*/
}
Thread.Sleep(2000);
}
}
catch (Exception err) { }
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
finalizeBleTest();
}
Thanks for any help!
I agree with the other answer that BackgroundWorker is not compatible with async/await. However, I disagree that the simplest solution is to remove async in favor of BackgroundWorker. IMO, the superior solution (also resulting in simpler code) is to remove BackgroundWorker in favor of async; specifically, to replace the outdated BackgroundWorker with the superior Task.Run:
// `worker` is now `Task`.
await Task.Run(() => callTestBLE());
finalizeBleTest();
where the signature of callTestBLE is async Task, not async void.
The problem here with the async and await. BackgroundWorker is a bit outdated and do not support asynchronous code. So when you await for testBLEAsync call, callTestBLE method finishes and at that moment you have your RunWorkerCompleted event called, while actual code continue to work in the background.
So simplest solution is to completely remove async/await from your code and everything should work as expected, or, alternatively, you can rewrite your code using tasks and task continuations.
Related
I am showing a modal dialog with my background worker, but it's becoming unresponsive when some long task is done on completed event of worker thread. Can someone tell me why it is happening and how I can fix this?
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var back = new BackgroundWorker();
back.DoWork += delegate
{
int i = 0;
while (i < 100)
{
Thread.Sleep(100);
i++;
}
};
back.RunWorkerCompleted += delegate
{
//running along task on UI therad
int i = 0;
while (i < 10000)
{
int j = 0;
while (j<10000)
{
label.Content = i.ToString();
j++;
}
i++;
}
msgbox.Close();
};
back.RunWorkerAsync();
msgbox.Title = "loading";
msgbox.Owner = this;
msgbox.ShowDialog();
}
You can't run any long-running method on the UI thread without blocking it. A single thread cannot both execute your while loop(s) and respond to user input simultaneously. This is impossible.
That's why you should execute any long-running method on a background, i.e. in the DoWork event handler in this case.
The RunWorkerCompleted event handler should only perform some quick UI related tasks, like for example updating a label or similar. Don't do anything heavy in there because then you will block the UI thread and your application will become unresponsive.
This part is executed in the UI thread:
back.RunWorkerCompleted += delegate
{
//running UI therad
int i = 0;
while (i < 100)
{
Thread.Sleep(100);
label.Content = i.ToString();
i++;
}
msgbox.Close();
};
When you call Thread.Sleep(100); you put the whole UI thread to sleep. Meaning, that your UI will sleep for 10 seconds.
I am trying to create a new Thread and put it to sleep in some occasions, but when I do it the main thread sleep, not only the one that I had created. I am using a Dispatcher.BeginInvoke but this is only to "give a permission" from the main thread to access to the method.
It works because it does not give me an InvalidOperationException, but the "focus" of the created thread losses when the linked method start.
I think I should use a ManualResetEvent to wait for the created Thread, but I do not know how doing it. I have been looking for possible solutions but no one works.
I think this should be easy but i cannot do it. The following code is Below:
void EmpujeDispatcher(object objeto)
{
this.Dispatcher.BeginInvoke(new Action<object>(Empuje), objeto);
}
private void Empuje(object objeto)
{
Thread.Sleep(2000); MessageBox.Show("This should not freeze the window");
Canvas Bacteria = objeto;
double PosX = Canvas.GetLeft(Bacteria);//PosiciĆ³n del sender
double PosY = Canvas.GetTop(Bacteria);//Lo mismo
Bacterias BacteriaInstancia = InstanciaBacterias[Bacteria.Uid];//Se busca la bacteria para relacionarla con al instancia
BacteriaInstancia.posX = PosX;
BacteriaInstancia.posY = PosY;
// BacteriaInstancia.Moverse();
if (BacteriaInstancia.momemtum <= 0)
{
Canvas.SetTop(Bacteria, PosY); Canvas.SetLeft(Bacteria, PosX);//Para el empuje
dispatcherTimer.Stop();
}
else
{ //Rebote:
BacteriaInstancia.Posicion();
PosX = BacteriaInstancia.posX;
PosY = BacteriaInstancia.posY;
if (PosX + Bacteria.Width >= CanvasSimulador.Width) { BacteriaInstancia.direccionAnterior = BacteriaInstancia.direccion; BacteriaInstancia.direccion = 1; }
if (PosX <= 0) { BacteriaInstancia.direccionAnterior = BacteriaInstancia.direccion; BacteriaInstancia.direccion = 3; }
if (PosY + Bacteria.Height >= CanvasSimulador.Height) { PosY = CanvasSimulador.Height - Bacteria.Height; BacteriaInstancia.direccionAnterior = BacteriaInstancia.direccion; BacteriaInstancia.direccion = 2; }
if (PosY <= 0) { PosY = 1; BacteriaInstancia.direccionAnterior = BacteriaInstancia.direccion; BacteriaInstancia.direccion = 4; }
Canvas.SetTop(Bacteria, PosY); Canvas.SetLeft(Bacteria, PosX);
BacteriaInstancia.momemtum = Math.Sqrt(Math.Pow(BacteriaInstancia.Vfx, 2) + Math.Pow(BacteriaInstancia.Vfy, 2));
ControlFlujo = BacteriaInstancia.momemtum;
}
private void EmpujeEvent(object sender, MouseButtonEventArgs e)
{
Thread TimerClockThread = new Thread(new ParameterizedThreadStart(EmpujeDispatcher));
TimerClockThread.IsBackground = true;
TimerClockThread.Start(sender);
}
This is not exacly the code because in this one Dispatcher does not have any sense, if I create the Thread without dispatcher
TimerClockThread = new Thread( new ParameterizedThreadStart(Empuje));
It works well... because it's a MessageBox, but in the original I have a lot of code inside of the "Empuje".
Thanks for your attention and hopefully you can help me :)
Your Dispatcher.Invoke forces your Empuje method to be called on the UI thread. If you want to update the screen, you should move the call to the background thread:
TimerClockThread = new Thread( new ParameterizedThreadStart(Empuje));
private void Empuje(object objeto)
{
Thread.Sleep(2000);
Dispatcher.BeginInvoke(new Action(() => {
MessageBox.Show("This should not freeze the window");
}));
//........ Do stuff.....
}
In modern C# with async however, you can remove all code and instead write:
private async void EmpujeEvent(object sender, MouseButtonEventArgs e)
{
await Task.Delay(2000);
MessageBox.Show(...);
}
Your function EmpujeDispatcher is using the same dispatcher that your GUI thread is associated with. That means you are telling the dispatcher to execute Empuje asynchronously, unfortunately it is executed on the GUI thread. At least that's what I think.
A BackgroundWorker is quite fit for this task. The shortest piece of code to implement it looks like this:
var worker = new BackgroundWorker();
worker.DoWork += (s,e) =>
{
Thread.Sleep(2000);
// Do Stuff...
};
worker.RunWorkerAsync();
Searching SO will yield a plethora of Q&A about the BackgroundWorker (i.e. this or this)
I have a problem with background worker.
I don't know how exactly to describe it.
Actually its a game and with the background worker ever x milisecs i update the progress bar and check if anyone has lost/won or the time is up.
If someome has win the game ends.
If both players have lost/time is up the game goes to the next round.
The ploblem occurs when both players have lost. The method NextRound in the SetTime method,
runs twice.
Here is the code:
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.SetTime(e.ProgressPercentage);
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(3000);
BackgroundWorker worker = sender as BackgroundWorker;
int tick = ProgLib.maxTime * 10;
for (int i = 1; i <= 100; i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
Thread.Sleep(tick);
worker.ReportProgress(i);
}
}
}
private void SetTime(double k)
{
this.time.Bar1.Value = k;
this.time.Bar2.Value = k;
if (k >= 100 || (Gallery1.hasLost() && Gallery2.hasLost()) || ((Gallery1.isWinner() || Gallery2.isWinner())))
{
if (bw == null)
return;
bw.CancelAsync();
bw.Dispose();
bw = null;
saveData();
ProgLib.isAnyoneWinner(Gallery1.isWinner(), Gallery2.isWinner());
if (ProgLib.gameHasended())
{
gameHasEnded();
}
else
{
next_round();
}
}
}
private void next_round()
{
Thread nextRoundThread = new Thread((Object Send) =>
{
MainThread.Send((Object send) =>
{
Gallery1.hidePanel.Visibility = Visibility.Visible;
Gallery2.hidePanel.Visibility = Visibility.Visible;
ProgLib.nextLetter();//goes to next letter
LetterToPlay1.setLetter(ProgLib.getArrabicLetter(ProgLib.getCurentLetter()));//sets the next letter
LetterToPlay2.setLetter(ProgLib.getArrabicLetter(ProgLib.getCurentLetter()));
}, null);
Thread SoundThread = new Thread((Object send) =>
{
//Here Must Delay enought so the animation stops the hear the bell and the the letter, and then the game starts
Thread.Sleep(1800);
ProgLib.playOtherSound(ProgLib.Sounds.Chimes);//Bell Sound
Thread.Sleep(100);
//ProgLib.PlayLetterSound(ProgLib.getCurentLetter());//Letter Sound
ProgLib.playOtherSound(ProgLib.Sounds.Cat_Yawn);//TestOnly
});
SoundThread.IsBackground = true;
SoundThread.Start();
Thread.Sleep(3000);
MainThread.Send((Object send) =>
{
Gallery1.refresh();//galleries refresh so that the magician hides.
Gallery2.refresh();//
Gallery1.hidePanel.Visibility = Visibility.Hidden;//hide the Big Magician of mistakes
Gallery2.hidePanel.Visibility = Visibility.Hidden;
}, null);
});
nextRoundThread.IsBackground = true;
nextRoundThread.Start();
bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerAsync();
}
There's a threading race in your code. Your worker will call ReportProgress() and immediately iterate the loop. Racing past the CancellationPending property check and falling asleep again.
Your SetTime() method runs later. And calls CancelAsync() but that doesn't have any effect at all since the worker is sleeping. Not until it wakes up again, calls ReportProgress() again, iterates the loop and then sees CancellationPending set to true.
Your SetTime() method will be called again, even though you've already ended the game.
Threading is rife with problems like this. A band-aid is to check for CancellationPending after the Sleep() call. Which works 99.999% of the time. Getting to 100% requires a pretty drastic rewrite that uses proper locking.
I am using threading in my code , thread are created using function:
private void InitializeBackgoundWorkers()
{
for (int f = 0; f < maxThreads; f++)
{
listBox1.Items.Insert(0, "Starting Thread : " + (f + 1));
threadArray[f] = new BackgroundWorker();
threadArray[f].DoWork +=
new DoWorkEventHandler(backgroundWorkerFiles_DoWork);
threadArray[f].RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(backgroundWorkerFiles_RunWorkerCompleted);
threadArray[f].ProgressChanged +=
new ProgressChangedEventHandler(backgroundWorkerFiles_ProgressChanged);
threadArray[f].WorkerReportsProgress = true;
threadArray[f].WorkerSupportsCancellation = true;
}
}
And the doevent is something like :
private void backgroundWorkerFiles_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
int flag = 0;
while (rowCounter < allPostingRows.Tables[0].Rows.Count && flag == 0)
{
for (int i = 0; i < maxThreads; i++)
{
if (threadArray[i].CancellationPending == true)
{
flag = 1;
threadArray[i].CancelAsync();
worker.ReportProgress(0, "Thread Paused:");
}
}
if (flag == 0)
{
//perform work here
}
}
}
And on button i try to cancel the threads using:
for (int i = 0; i < maxThreads; i++)
{
threadArray[i].CancelAsync();
}
Am i cancelling the thread correctly? As when they get canceled i see the line in listbox saying thread cancelled so it does go to the cancellation code but after some time it restarts
Thank you
I don't think you really understand BackgroundWorker. The DoWork event handler is supposed to be a handler for one unit of work. DoWork is called with one thread. It doesn't make sense to call CancelAsync from within a DoWork handler--that's independent of any and all other BackgroundWorker's. Within the DoWork handler it should only be checking one CancellationPending, the sender's (once cast to BackgroundWorker, in your case worker).
But, otherwise, calling CancelAsync from the UI is the correct way to cancel a particular BackgroundWorker.
Background workers are not "thread"s. You''re not cancelling a thread, you're cancelling the worker--which allows the DoWork handler a chance to exit before it is done it's work.
In a win form application, I have an array of threads which are started like this:
bool stop = false;
Thread[] threads = new Thread[10];
for (int i = 0; i < threads.Length; i++)
threads[i] = new Thread(new ThreadStart(Job));
// How to make sure all threads have exited, when the boolean = false
void Job()
{
while (!stop)
// Do something
}
Now if user press STOP, the boolean value for stop will set to true, so threads exit the Job method one after another. How can I make sure all threads are exited?
NOTE: I need traditional threading for my case and TaskLibrary doesn't fit my scenario.
Use the Join method to check if all threads have stopped.
foreach (var t in threads)
{
t.Join();
}
Have you thought about using BackgroundWorkers instead? You said "traditional threads"..I'm not exactly sure what you mean so I don't know if this is a valid proposal or not, but here it is anyways in case Join() doesn't solve your problem
BackgroundWorker[] workers = new BackgroundWorker[10];
bool allThreadsDone = false;
// initialize BackgroundWorkers
for (int i = 0; i < 10; i++)
{
workers[i] = new BackgroundWorker();
workers[i].WorkerSupportsCancellation = true;
workers[i].RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
workers[i].DoWork += new DoWorkEventHandler(AlgorithmsUI_DoWork);
workers[i].RunWorkerAsync();
}
// thread entry point..DoWork is fired when RunWorkerAsync is called
void AlgorithmsUI_DoWork(object sender, DoWorkEventArgs e)
{
while (!stop)
// do something
}
// this event is fired when the BGW finishes execution
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
bool threadsStillRunning = false;
foreach (BackgroundWorker worker in workers)
{
if (worker.IsBusy)
{
threadsStillRunning = true;
break;
}
}
if (!threadsStillRunning)
allThreadsDone = true;
}
protected override OnFormClosing(FormClosingEventArgs e)
{
if (!allThreadsDone)
{
e.Cancel = true;
MessageaBox.Show("Threads still running!");
}
}
This should prevent your form from closing if any threads are still running.
I'm not sure if this is what you're looking for, but here's a simple solution I used back in .NET 3.0 to make sure a large but deterministic number of threads had completed before continuing:
Global:
AutoResetEvent threadPoolComplete = new AutoResetEvent(false);
static int numThreadsToRun;
As you activate the threads:
numThreadsToRun = [number of threads];
[start your threads];
threadPoolComplete.WaitOne();
At the end of each thread's code:
if (Interlocked.Decrement(ref numThreadsToRun) == 0)
{
threadPoolComplete.Set();
}