I'm new to multi-threading and I'm to understand how the AutoResetEvent works.
I'm trying to implement an optimization process where I'm sending data between two different softwares and I'm using two threads: the Main thread where I'm modifying and sending information and a Receiving Thread, always running in the background, waiting to catch the message with the results from the Sent Info. To implement that, after a message is sent, I want the main thread to wait until the receiver thread receives back the result and triggers the event that allows the main thread to continue where it left off.
Here is a simplified version of my code:
// Thread 1 - MAIN
static AutoResetEvent waitHandle = new AutoResetEvent(false);
void foo()
{
for (int i = 0; i < 5; i++)
{
// ... Modify sendInfo data
// Send data to other software
SendData(sendInfo);
// Wait for other software to process data and send back the result
waitHandle.WaitOne();
// Print Result
print(receivedData);
// Reset AutoResetEvent
waitHandle.Reset();
}
}
/////////////////////////////
// Thread 2 - Receiver thread (running in the background)
private event EventHandler MessageReceived;
// ... Code for triggerring MessageReceived event each time a message is received
private static void OnMessageReceived(object sender, EventArgs e)
{
waitHandle.Set();
}
My question is:
Can you repeatedly use an AutoResetEvent in a loop like this? Am I using it correctly?
I'm pretty sure my send/receive loop is working properly, with the MessageReceived event succesfully triggered shortly after the sent message. But while my code works fine for a single iteration, it gets stuck on multiple iterations and I'm not sure why. Any suggestions?
Related
I have a five step Wizard type application. There is a main GUI thread, and a background thread that processes messages from a server (among other things). Once the user clicks through step 2 of the Wizard, the GUI thread fires off a request to the server to send a list of data meeting query conditions. The background thread listens for the server's response. The server will first send a message containing an integer that defines the number of records that met the query conditions. Then the server will send each query record one by one.
Once the user clicks "Finish" on the Wizard, my GUI thread just assumes we got all the messages and wraps up its processes. Normally this is fine because the messages from the server come very quickly. However, sometimes not all the messages will be received by the time the user clicks Finish, and this causes problems.
My question is, once the user clicks Finish, what is the proper way to make the GUI thread wait for the background thread to agree that all messages have been received before the GUI thread starts its wrap up procedures?
Below is some pseudocode of what is probably a very dumb way to do it, but I'm looking for the "right" way, or "good design" way.
BACKGROUND THREAD:
void ReceiveMessage() {
global "ready" flag = false
if MessageType == GetCount, set expectedCount = message.Count
if MessageType == Data
currentCounter++
process message...
if currentCounter = expectedCount, set global flag to TRUE
}
GUI THREAD:
On_Finish_Clicked() {
while (!global ready flag) {
sleep
}
finish processing...
}
Basically the same as TheGeneral, except I'm using ManualResetEvent and waiting in a button click handler using Async/Await:
private ManualResetEvent mre = new ManualResetEvent(false);
private void btn2_Click(object sender, EventArgs e)
{
btn2.Enabled = false;
Task.Run(() =>
{
// ... long running tasks in here ...
Thread.Sleep(15000);
// signal the app that it's complete
mre.Set();
});
}
private async void btnFinish_Click(object sender, EventArgs e)
{
btnFinish.Enabled = false;
await Task.Run(() => {
mre.WaitOne();
});
this.Close();
}
Given your limitations, you might just need an AutoResetEvent or similar to signal completion, it's better than using a while loop chewing up pesky cpu cycles
Represents a thread synchronization event that, when signaled, resets
automatically after releasing a single waiting thread.
Example
var resetEvent = new AutoResetEvent(false);
Task.Run(() =>
{
Console.WriteLine("Task is waiting");
Thread.Sleep(1000);
Console.WriteLine("Calling Set");
resetEvent.Set();
});
Console.WriteLine("Waiting for sync");
resetEvent.WaitOne();
Console.WriteLine("yehaa");
Output
Waiting for sync
Task is waiting
Calling Set
yehaa
In short, the idea is create the Reset Event, when all the conditions are met call Set. At any point, call WaitOne to block the main thread (maybe on finish click). This will have the affect of waiting for your background task to finish, and will also give the desired results if the tasks finishes first.
From what I've read, beginReceive is considered superior to receive in almost all cases (or all?). This is because beginReceive is asynchronous and waits for data to arrive on a seperate thread, thereby allowing the program to complete other tasks on the main thread while waiting.
But with beginReceive, the callback function still runs on the main thread. And so there is overhead with switching back and forth between the worker thread and the main thread each time data is received. I know the overhead is small, but why not avoid it by simply using a separate thread to host a continuous loop of receive calls?
Can someone explain to me what is inferior about programming with the following style:
static void Main()
{
double current_temperature = 0; //variable to hold data received from server
Thread t = new Thread (UpdateData);
t.start();
// other code down here that will occasionally do something with current_temperature
// e.g. send to a GUI when button pressed
... more code here
}
static void UpdateData()
{
Socket my_socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
my_socket.Connect (server_endpoint);
byte [] buffer = new byte[1024];
while (true)
my_socket.Receive (buffer); //will receive data 50-100 times per second
// code here to take the data in buffer
// and use it to update current_temperature
... more code here
end
}
I'm currently working on a program's subsystem that requires writing data to disk. I've implemented this as a multithreaded Producer-Consumer model that generates packets of data in one thread, puts them in a queue and writes them to disk in another thread.
The program has to use minimal CPU resources, so to avoid the write thread idling while it is waiting for a packet of data to arrive, I extended the ConcurrentQueue class to trigger an Event when a packet has been added to the queue, so that the write function is only active when there is data available. Here's the generate function:
while (writeData)
{
for (int i = 0; i < packetLength; i++)
{
packet.data[i] = genData();
}
packet.num++;
// send packet to queue to be written
// this automatically triggers an Event
queue.Enqueue(packet);
}
My problem is that I haven't been able to figure out how to assign the Event Handler (ie: the write operation) to a seperate thread - as far as I'm aware, Event Handlers in C# are run on the same thread that triggered the event.
I've thought about using the ThreadPool, but I'm unsure as to whether the packets in the queue would be written sequentially or in parallel. Writing packets in parallel would not work in this context, as they are all being written to the same file and have to be written in the order they arrive in. This is what I have so far:
private void writeEventCatch(object sender, EventArgs e)
{
// catch event and queue a write operation
ThreadPool.QueueUserWorkItem(writeToDisk);
}
private void writeToDisk(Object stateInfo)
{
// this is a struct representing the packet
nonCompBinData_P packet;
while (queue.TryDequeue(out packet))
{
// write packet to stream
serialiser.Serialize(fileStream, packet);
packetsWritten++;
}
}
while (queue.TryDequeue(out packet)) will quit as long as there are no packets to dequeue. what you need to do is start single thread for writing operation deque work items and write data to disk. items will be dequeued one by one and in order they arrive.
Suppose you have a search textbox and have a search algorithm attached to the TextChanged event, that runs with a BackgroundWorker. If there comes a new character in the textbox, i need to cancel the previous search and run it again.
I tried using events in between the main thread and the bgw, from this previous question, but I still get the error "currently busy and cannot run multiple tasks concurrently"
BackgroundWorker bgw_Search = new BackgroundWorker();
bgw_Search.DoWork += new DoWorkEventHandler(bgw_Search_DoWork);
private AutoResetEvent _resetEvent = new AutoResetEvent(false);
private void txtSearch_TextChanged(object sender, EventArgs e)
{
SearchWithBgw();
}
private void SearchWithBgw()
{
// cancel previous search
if (bgw_Search.IsBusy)
{
bgw_Search.CancelAsync();
// wait for the bgw to finish, so it can be reused.
_resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
}
// start new search
bgw_Search.RunWorkerAsync(); // error "cannot run multiple tasks concurrently"
}
void bgw_Search_DoWork(object sender, DoWorkEventArgs e)
{
Search(txtSearch.Text, e);
}
private void Search(string aQuery, DoWorkEventArgs e)
{
int i = 1;
while (i < 3) // simulating search processing...
{
Thread.Sleep(1000);
i++;
if (bgw_Search.CancellationPending)
{
_resetEvent.Set(); // signal that worker is done
e.Cancel = true;
return;
}
}
}
EDIT To reflect answers. DonĀ“t reuse the BackgroundWorker, create a new one:
private void SearchWithBgw()
{
if (bgw_Search.IsBusy)
{
bgw_Search.CancelAsync();
_resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
bgw_Search = new BackgroundWorker();
bgw_Search.WorkerSupportsCancellation = true;
bgw_Search.DoWork += new DoWorkEventHandler(bgw_Search_DoWork);
}
bgw_Search.RunWorkerAsync();
}
When the _resetEvent.WaitOne() call completes, the worker thread isn't actually done. It is busy returning from DoWork() and waiting for an opportunity to run the RunWorkerCompleted event, if any. That takes time.
There is no reliable way to ensure the BGW is completed in a synchronous way. Blocking on IsBusy or waiting for the RunWorkerCompleted event to run is going to cause deadlock. If you really want to use only one bgw then you'll have to queue the requests. Or just don't sweat the small stuff and allocate another bgw. They cost very little.
Create a new background worker if the old one exists.
private void SearchWithBgw()
{
// cancel previous search
if (bgw_Search.IsBusy)
{
bgw_Search.CancelAsync();
// wait for the bgw to finish, so it can be reused.
_resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
BackgroundWorker bgw_Search = new BackgroundWorker();
bgw_Search.DoWork += new DoWorkEventHandler(bgw_Search_DoWork);
}
// start new search
bgw_Search.RunWorkerAsync(); // error "cannot run multiple tasks concurrently"
}
Also I know you put fake code in, but you want to make sure you set _resetEvent when the code completes normally too.
Do not reuse a Backgroundworker. It is a cheap resource, it is not a Thread.
make sure your Bgw code stops, yours looks OK. The Bgw will release the Thread to the pool.
but in the mean time, create a new Task/Bgw for a new job.
You may want to unsubscribe your Completed event from the old Bgw.
I think you should consider not cancelling the background worker.
If you cancel requests and the user types faster than your server returns queries, he will not see suggestions until he is finished typing.
In interactive scenarios like this, It could be better to show responses that run behind with what the user's typing. Your user will know he can stop typing if the word he has in mind is your suggestions list.
This will be also better for your server when it is busy, because instead of many cancelled requests, who will cost something but that are ultimately not shown, there will be fewer requests whose response you actually use.
I ran into similar issues with (3d) rendering applications, where the beginner's mistake is to cancel and rerender on every mousemove. This lead to a lot of computation and little interactive feedback.
I am developing a application in C# which is getting data from Serial Port, Processing it and showing it to UI.
The data is coming very fast between 5-50ms speed. Before I was not using any threads. and so application was relying on single App thread which was getting data from Serial Port, Processing data and showing it to UI. and It was loosing some data.
Then I started implementing BackgroundWorker thread to remove some overhead on single thread and thinking of good performance. And Now I am getting "This BackgroundWorker is currently busy and cannot run multiple tasks concurrently" error. I think Thread is not able to cope up with the speed the data coming from Serial port. and So throwing error on executing "backgroundWorker1.RunWorkerAsync(data);". I need some suggestions what's the better approach to implement such kind of scenario?
geofftnz is correct, and I'll expand a bit for you. You should only start the background worker once, and have it feed the data back to the GUI thread using ReportProgress. worker thread would look something like this.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackGroundWorker;
while(!e.CancellationPending)
{
ResultObject ro = new ResultObject(); // your own type here, obviously
//Process and store some data in ro;
worker.ReportProgress(0, ro);
//Do not modify ro after reporting progress to avoid threading issues
}
}
From the GUI, register to the ProgressChanged event, and only start the worker one time.
The problem is that you're calling backgroundWorker1.RunWorkerAsync() before it's finished with its previous operation.
What you probably want to have is a single thread reading the serial port, buffering the data and notifying the main UI thread that data is available.
Try adding this in, to make sure the background worker is only running one job at a time.
if(!backgroundWorker1.IsBusy)
backgroundWorker1.RunWorkerAysnc();
You also have the ability to cancel the current job, here is a code sample
private void WhereBackworkerStarts()
{
backgroundWorker.WorkerSupportsCancellation = true;
if (backgroundWorker.IsBusy)
backgroundWorker.CancelAsync();
else
backgroundWorker.RunWorkerAsync();
}
// Events
static void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
for(int i = 0; i < int.MaxValue; i++)
{
if (backgroundWorker.CancellationPending)
{
e.Cancel = true;
return;
}
// Do work here
}
e.Result = MyResult;
}
static void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Deal with results
}