How to wait for background worker to finish processing? - c#

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.

Related

On a blocking background worker and Application.DoEvents

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);

How can I display a loading control while a process is waiting for be finished?

I decided to use this third-party component to make a simple loading control in my windows form.
http://www.codeproject.com/Articles/14841/How-to-write-a-loading-circle-animation-in-NET
This works fine when turns on and off changing the property "Active" to true or false in a single request (one per time). The problem is when a process is waiting to be served, and I pretend to Active the loadingControl before the process starts and turn off when I "think" that the process has to be finished. When I do it, the image loading is shown as a static image. (Without animation).
I'm sorry for this question, I'm new in C#. But I think that I need to use Threads or something similar.
So my general code is this:
using [libraries here]...;
namespace [namespace here]
{
Public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.loadingCircle1.Visible = false;
}
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(showLoading));
this.loadingCircle1.Visible = true;
t.Start();
//Import an Excel
t.Abort();
}
public void showLoading()
{
loadingCircle1.Active = true;
loadingCircle1.RotationSpeed = 10;
}
}
}
But Always the Loading shows as a static image without the animation.
You create a thread, which simply sets two properties and then ends. The t.Abort will probably do nothing, since the thread will have been exited by that time. Even worse, you import the excel file on the UI thread, which blocks any animation and freezes the complete UI.
This is how you should make it:
Remark: Of course if your form is responsive, you must disable/enable the controls and prepare to the case what happens if your form is being closed during the load.
1. Using threads
If you really want to explicitly use threads, do it like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private Thread workerThread = null;
private void btnImport_Click(object sender, EventArgs e)
{
// start the animation (I used a progress bar, start your circle here)
progressBar1.Visible = true;
progressBar1.Style = ProgressBarStyle.Marquee;
// start the job and the timer, which polls the thread
btnImport.Enabled = false;
workerThread = new Thread(LoadExcel);
workerThread.Start();
timer1.Interval = 100;
timer1.Start();
}
private void LoadExcel()
{
// some work takes 5 sec
Thread.Sleep(5000);
}
private void timer1_Tick(object sender, EventArgs e)
{
if (workerThread == null)
{
timer1.Stop();
return;
}
// still works: exiting
if (workerThread.IsAlive)
return;
// finished
btnImport.Enabled = true;
timer1.Stop();
progressBar1.Visible = false;
workerThread = null;
}
}
2. Background worker
The BackgroundWorker can throw an event when it is finished:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.DoWork += BackgroundWorker1_DoWork;
backgroundWorker1.RunWorkerCompleted += BackgroundWorker1_RunWorkerCompleted;
}
private void btnImport_Click(object sender, EventArgs e)
{
// start the animation
progressBar1.Visible = true;
progressBar1.Style = ProgressBarStyle.Marquee;
// start the job
btnImport.Enabled = false;
backgroundWorker1.RunWorkerAsync();
}
private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
LoadExcel();
}
private void BackgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
btnImport.Enabled = true;
progressBar1.Visible = false;
}
private void LoadExcel()
{
// some work takes 5 sec
Thread.Sleep(5000);
}
}
3. Using async-await
This is the simplest one.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void btnImport_Click(object sender, EventArgs e)
{
// start the waiting animation
progressBar1.Visible = true;
progressBar1.Style = ProgressBarStyle.Marquee;
// simply start and await the loading task
btnImport.Enabled = false;
await Task.Run(() => LoadExcel());
// re-enable things
btnImport.Enabled = true;
progressBar1.Visible = false;
}
private void LoadExcel()
{
// some work takes 5 sec
Thread.Sleep(5000);
}
}
I'd recommend to use async/await (for C# 5.0):
private void button1_Click(object sender, EventArgs e){
ImportAsync();
}
private async Task ImportAsync(){
// UI-thread
showLoading();
this.loadingCircle1.Visible = true;
// wait until task will be finished
await Task.Run(() => {
// different non-blocking thread for all the hard work, but without UI-stuff
// import an Excel
});
// going back to UI-thread
this.loadingCircle1.Visible = false;
}

How to stop a for loop using a button?

I have a loop that I would like to stop using a button.
Edited for better understanding:
I do realize that you cannot stop a button while a loop was running since it will not work as long as that current UI is running. What I'm really asking for is the most efficient way of creating a thread or using BGWorker to stop this. I have seen some methods, but most of them are for Java and not C#.
What I would like to do is:
private void start_Click(object sender, EventArgs e)
{
for(int i = 0; i < nums; i++)
{
doSomething();
}
}
private void stop_Click(object sender, EventArgs e)
{
stops start_Click()
}
You can't do that. For starters, the for loop is running synchronously on the UI thread, which means you won't even be able to click the "Stop" button.
Hence, you need to move the operations of the for loop onto another thread, which means you likely won't be using a for loop at all. You need to think about how the code inside actually needs to be executed, then based on how you are doing the processing, you can implement the "Stop" button.
A very simple way to do this would be to just:
new Thread(() =>
{
int i = 0;
while (!stop && i < num)
{
doSomething();
i++;
}
}).Start();
And set stop to stop the processing loop. In a more realistic scenario, you could queue up functions that you want to process, then stop dequeuing via a similar method. Unfortunately, its hard to reccommend a setup without knowing more details.
Any solution based on your code will also have the problem of the current doSomething() completing execution (which could take a while). Again, without more info, its hard to say what the best approach to fixing that is.
To keep your UI responsive to be able to cancel the running operation you can use a backgrounworker.
The backgroundworker does the work in an other thread while keeping your UI responsive:
private readonly BackgroundWorker _backgroundWorker;
public Form1()
{
InitializeComponent();
_backgroundWorker = new BackgroundWorker
{
WorkerSupportsCancellation = true
};
_backgroundWorker.DoWork += backgroundWorker_DoWork;
_backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
Disposed += Form1_Disposed;
}
private void Form1_Disposed(object sender, EventArgs e)
{
_backgroundWorker.Dispose();
}
private void StartLoop()
{
if ( !_backgroundWorker.IsBusy )
{
_backgroundWorker.RunWorkerAsync();
}
}
private void StopLoop()
{
_backgroundWorker.CancelAsync();
}
private void backgroundWorker_DoWork( object sender , DoWorkEventArgs e )
{
var backgroundWorker = ( BackgroundWorker ) sender;
for ( var i = 0; i < 100; i++ )
{
if ( backgroundWorker.CancellationPending )
{
e.Cancel = true;
return;
}
// Do Work
}
}
private void backgroundWorker_RunWorkerCompleted( object sender , RunWorkerCompletedEventArgs e )
{
if ( e.Cancelled )
{
// handle cancellation
}
if ( e.Error != null )
{
// handle error
}
// completed without cancellation or exception
}
IMHO, it's likely the best approach here is to convert your work to an asynchronous operation and then use the async/await idiom for the loop. E.g.:
private bool _stopLoop;
private async void start_Click(object sender, EventArgs e)
{
_stopLoop = false;
for(int i = 0; i < nums && !_stopLoop; i++)
{
await Task.Run(() => doSomething());
}
}
private void stop_Click(object sender, EventArgs e)
{
_stopLoop = true;
}
This allows the loop itself to execute in the UI thread where the _stopLoop variable is being managed, but without actually blocking the UI thread (which among other things would prevent the "Stop" button from being clicked).
Unfortunately, you didn't provide details about how doSomething() works. It's possible there's a good way to convert that whole method to be an async method, but I can't comment on that without the actual code.
Note that this approach will only interrupt the loop at a point in between each operation. If you want to be able to interrupt the doSomthing() operation itself, you'll have to provide a mechanism for that. One likely approach would be to use CancellationSource and CancellationToken, which provides a convenient way to express cancellation semantics.
Try using an async/await approach. It's quite easy!
public partial class MyForm : Form
{
public MyForm()
{
InitializeComponent();
}
private CancellationTokenSource _tokenSource;
private async void start_Click(object sender, EventArgs e)
{
if (_tokenSource != null)
return;
_tokenSource = new CancellationTokenSource();
var ct = _tokenSource.Token;
await Task.Factory.StartNew(() =>
{
for (; ; )
{
if (ct.IsCancellationRequested)
break;
doSomething();
}
}, ct);
_tokenSource = null;
}
private int _labelCounter;
private void doSomething()
{
// do something
Invoke((Action)(() =>
{
myLabel.Text = (++_labelCounter).ToString();
}));
}
private void stop_Click(object sender, EventArgs e)
{
if (_tokenSource == null)
return;
_tokenSource.Cancel();
}
}
try this :
bool stop=false;
private void start_Click(object sender, EventArgs e)
{
for(int i = 0; i < nums&& !bool; i++)
{
doSomething();
}
}
and in the click event
set
stop=true;

Cant understand: using lock () but never executing code

I'm learning about threads in C#, and i get this behavior that i cant understand.
The code simulates I/O operations, like files or serial port, where only one thread can access it at time, and it blocks until finishes.
Four threads are started. Each performs just a count. It works ok, i can see on the form the counts growing. But there is a button to count from the form thread. When i push it, the main thread freezes. The debugger shows that the others threads keep counting, one by one, but the form thread never gets access to the resource.
1) Why the lock(tty) from the form thread never gets access to it, when the others threads has no problem ?
2) Is there a better way to do this type of synchronization ?
Sorry about the big code:
public class MegaAPI
{
public int SomeStupidBlockingFunction(int c)
{
Thread.Sleep(800);
return ++c;
}
}
class UIThread
{
public delegate void dlComandoMaquina();
public class T0_SyncEvents
{
private EventWaitHandle _EventFechar; // Exit thread event
public T0_SyncEvents()
{
_EventFechar = new ManualResetEvent(false);
}
public EventWaitHandle EventFecharThread // Exit thread event
{
get { return _EventFechar; }
}
}
public class T0_Thread
{
private T0_SyncEvents _syncEvents;
private int _msTimeOut;
private dlComandoMaquina _ComandoMaquina;
public T0_Thread(T0_SyncEvents e, dlComandoMaquina ComandoMaquina, int msTimeOut)
{
_syncEvents = e;
_msTimeOut = msTimeOut;
_ComandoMaquina = ComandoMaquina;
}
public void VaiRodar() // thread running code
{
while (!_syncEvents.EventFecharThread.WaitOne(_msTimeOut, false))
{
_ComandoMaquina();
}
}
}
}
public partial class Form1 : Form
{
MegaAPI tty;
UIThread.T0_Thread thr1;
UIThread.T0_SyncEvents thrE1;
Thread Thread1;
int ACount1 = 0;
void UIUpdate1()
{
lock (tty)
{
ACount1 = tty.SomeStupidBlockingFunction(ACount1);
}
this.BeginInvoke((Action)delegate { txtAuto1.Text = ACount1.ToString(); });
}
UIThread.T0_Thread thr2;
UIThread.T0_SyncEvents thrE2;
Thread Thread2;
int ACount2 = 0;
void UIUpdate2()
{
lock (tty)
{
ACount2 = tty.SomeStupidBlockingFunction(ACount2);
}
this.BeginInvoke((Action)delegate { txtAuto2.Text = ACount2.ToString(); });
}
UIThread.T0_Thread thr3;
UIThread.T0_SyncEvents thrE3;
Thread Thread3;
int ACount3 = 0;
void UIUpdate3()
{
lock (tty)
{
ACount3 = tty.SomeStupidBlockingFunction(ACount3);
}
this.BeginInvoke((Action)delegate { txtAuto3.Text = ACount3.ToString(); });
}
UIThread.T0_Thread thr4;
UIThread.T0_SyncEvents thrE4;
Thread Thread4;
int ACount4 = 0;
void UIUpdate4()
{
lock (tty)
{
ACount4 = tty.SomeStupidBlockingFunction(ACount4);
}
this.BeginInvoke((Action)delegate { txtAuto4.Text = ACount4.ToString(); });
}
public Form1()
{
InitializeComponent();
tty = new MegaAPI();
thrE1 = new UIThread.T0_SyncEvents();
thr1 = new UIThread.T0_Thread(thrE1, UIUpdate1, 500);
Thread1 = new Thread(thr1.VaiRodar);
Thread1.Start();
thrE2 = new UIThread.T0_SyncEvents();
thr2 = new UIThread.T0_Thread(thrE2, UIUpdate2, 500);
Thread2 = new Thread(thr2.VaiRodar);
Thread2.Start();
thrE3 = new UIThread.T0_SyncEvents();
thr3 = new UIThread.T0_Thread(thrE3, UIUpdate3, 500);
Thread3 = new Thread(thr3.VaiRodar);
Thread3.Start();
thrE4 = new UIThread.T0_SyncEvents();
thr4 = new UIThread.T0_Thread(thrE4, UIUpdate4, 500);
Thread4 = new Thread(thr4.VaiRodar);
Thread4.Start();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
thrE1.EventFecharThread.Set();
thrE2.EventFecharThread.Set();
thrE3.EventFecharThread.Set();
thrE4.EventFecharThread.Set();
Thread1.Join();
Thread2.Join();
Thread3.Join();
Thread4.Join();
}
int Mcount = 0;
private void btManual_Click(object sender, EventArgs e)
{
Cursor.Current = Cursors.WaitCursor;
lock (tty) // locks here ! Never runs inside! But the other threads keep counting..
{
Mcount = tty.SomeStupidBlockingFunction(Mcount);
txtManual.Text = Mcount.ToString();
}
Cursor.Current = Cursors.Default;
}
}
I suspect you are hitting something with the Windows message loop and threading in WinForms. I don't know what that is, but here are a few pointers:
You can run the button's task in a backgroundWorker to keep the work off the UI thread. That solves the lock problem. Drag a BackgroundWorker from the toolbox and drop it on your Form in the designer, and hook up the event, i.e.:
this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
then switch your code in btManual_Click to call the background worker like this:
backgroundWorker1.RunWorkerAsync();
and then:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Mcount = tty.SomeStupidBlockingFunction(Mcount);
this.BeginInvoke((Action)delegate { txtManual.Text = Mcount.ToString(); });
}
I've left out the lock (tty) because I would rather see only one of these statements inside the function, rather than five of them outside. And instead of locking on tty, I would create a private variable like this:
public class MegaAPI
{
private object sync = new object();
public int SomeStupidBlockingFunction(int c)
{
lock (this.sync)
{
Thread.Sleep(800);
return ++c;
}
}
}
Everywhere else is then simplified, for example:
void UIUpdate1()
{
ACount1 = tty.SomeStupidBlockingFunction(ACount1);
this.BeginInvoke((Action)delegate { txtAuto1.Text = ACount1.ToString(); });
}
And since you can't run the background worker while it's still processing, here is a quick-and-dirty solution: disable the button while it's working:
this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
and then:
private void btManual_Click(object sender, EventArgs e)
{
this.btManual.Enabled = false;
backgroundWorker1.RunWorkerAsync();
}
and:
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.btManual.Enabled = true;
}
So I recommend:
Keep a single lock () statement
inside the function needing the
synchronization
Keep the lock object private
Run the work on a background worker
Mutexes do not provide fairness by default. They just guarantee that your process as a whole will make forward progress. It is the implementation's job to pick the best thread to get the mutex based on characteristics of the scheduler and so on. It is the coder's job to make sure that the thread that gets the mutex does whatever work the program needs done.
If it's a problem for you if the "wrong thread" gets the mutex, you are doing it wrong. Mutexes are for cases where there is no "wrong thread". If you need fairness or predictable scheduling, you need to use a locking primitive that provides it or use thread priorities.
Mutexes tend to act in strange ways when threads that hold them aren't CPU-limited. Your threads acquire the mutex and then deschedule themselves. This will lead to degenerate scheduling behavior just like the behavior you're seeing. (They won't break their guarantees, of course, but they will act much less like a theoretically perfect mutex that also provided things like fairness.)

Thread progress to GUI

I implemented threading in my application for scraping websites. After all the sites are scraped I want to process them.
form creates queueworker(which creates 2 workers and processes tasks).
After all the tasks are done I want to process them baack in the formthread.
At this point I accieved this with
public void WaitForCompletion()
{
// Enqueue one null task per worker to make each exit.
StopWorkers();
//wait for the workers to finish
foreach (Thread worker in workers)
{
worker.Join();
}
}
After a task is preformed I fire (from queueworker):
public event EventHandler<ProgressEvent> UrlScanned;
if (UrlScanned != null)
{
UrlScanned(this, new ProgressEvent(task.Name, 1));
}
And catch that event with:
urlscanner.UrlScanned += new EventHandler<ProgressEvent>(UrlScanningProgress);
private void UrlScanningProgress(object sender, ProgressEvent args)
{
if (pbarurlScan.InvokeRequired)
{
//don't use invoke when Thread.Join() is used! Deadlock
Invoke(new MethodInvoker(delegate() { UrlScanningProgress(sender, args);
//BeginInvoke(new MethodInvoker(delegate() { UrlScanningProgress(sender, args)};
}
else
{
pbarurlScan.Value++;
}
}
The problem is that the formthread gets blocked and with calling Invoke the whole application is now in a deadlock situation.
How can I give update to the formthread without having a deadlock and have an immidiate update (begininvoke occurs if the workers threads are done)
Why are you doing the Join? I would raise a callback at the end of each thread - perhaps decrementing a counter. When the counter gets to 0, then call back to the UI thread and do the "all done" code; something like:
using System.Windows.Forms;
using System.Threading;
using System;
class MyForm : Form
{
public MyForm()
{
Button btn = new Button();
Controls.Add(btn);
btn.Text = "Go";
btn.Click += btn_Click;
}
int counter;
void btn_Click(object sender, System.EventArgs e)
{
for (int i = 0; i < 5; i++)
{
Interlocked.Increment(ref counter);
ThreadPool.QueueUserWorkItem(DoWork, i);
}
}
void DoWork(object state)
{
for (int i = 0; i < 10; i++)
{ // send progress
BeginInvoke((Action)delegate { Text += state.ToString(); });
Thread.Sleep(500);
}
EndThread(); // this thread done
}
void EndThread()
{
if (Interlocked.Decrement(ref counter) == 0)
{
AllDone();
}
}
void AllDone()
{
Invoke((Action)delegate { this.Text += " all done!"; });
}
[STAThread]
static void Main()
{
Application.Run(new MyForm());
}
}
I fire a seperate event now when all tasks are done. I moved the logic for processing the tasks to a seperate function that will be called when receiving the AllUrlsScanned event.

Categories