I have looked at the answer in
How to block until an event is fired in c#
unfortunately, it does not provide a full answer that I can incorporate or make it work for my situation.
I am building an app that talks to a remote equipment through Serial port.
There is a protocol to be used whenever communication is established. It goes like this:
PC: Are you ready?
Hardware: Yes go ahead!
PC: Here is my question <insert question here>
PC: Did you get it?
Hardware: Yes, I got your question. Let me think
...
some time later
...
Hardware: Are you ready?
PC: yes, go ahead
Hardware: Here is your answer: <insert answer here>
I am currently using Sleep(1000) in-between messages to make sure the hardware did receive the message. But the problem is 1 second might be too much in a lot of cases. Because the hardware can reply as soon as 20 milliseconds, and at worst 800 milliseconds. I need to trigger an event whenever the hardware says it is ready, or when it asks if PC is ready.
Here is what I am hoping to do:
private event EventHandler HardwareSaysItIsReadyEvent;
private event EventHandler HardwareAsksIfPcIsReadyEvent;
static async Task Main(string[] args)
{
SetUpComPort();
HardwareSaysItIsReadyEvent += HardwareIsReadyEvent;
HardwareAsksIfPcIsReady += IsPcReadyEvent;
await AskHardwareIfReadyAndWaitForAnswer();
// assume the hardware will always answer with YES, but it may take time
await AskHardwareQuestion();
await AskHardwareIfQuestionWasReceivedAndWaitForAnswer();
}
private void SetUpComPort() { ... }
private async Task AskHardwareIfReadyAndWaitForAnswer()
{
/* I can send data to the hardware but how can I force
The process to wait here until the event is triggered,
or 1000 milliseconds expire?
*/
}
private async Task AskHardwareIfQuestionWasReceivedAndWaitForAnswer() { ... }
private void HardwareIsReadyEvent(object sender, EventArgs e)
{
// let the awaiter know so it may proceed to the question part
}
private void IsPcReadyEvent(object sender, EventArgs e)
{
// send a YES confirmation then let awaiter know so that it may proceed to receive answer
}
How can I suspend execution (without blocking UI) until a certain timeout, or an event is triggered?
Edit: I know how to trigger events based on text received from hardware, I did not show that part in my code. I don't need help triggering the event. I just need help telling program to send something and waiting for a reply or quitting after 1 second.
Edit 2: I am using SerialPort builtin class to do the talking with the hardware, and am already hooking up data received event in case the hardware is talking to the PC. That is how I am firing the events ServerSaysItIsReady and ServerIsAsking...
If your process is sequential (only one "question" at a time, always followed by event) then you can use single TaskComletionSource to achieve your goal:
private TaskCompletionSource _reply;
private async Task AskHardwareIfReadyAndWaitForAnswer() {
_reply = new TaskCompletionSource();
// now send a message to the hardware
// use separate task to indicate timeout
var timeoutTask = Task.Delay(1000);
// if timeoutTask completed first - then you waited for 1 second with no reply
bool isTimeout = await Task.WhenAny(_reply.Task, timeoutTask) == timeoutTask;
if (isTimeout) {
_reply = null;
}
}
private void HardwareIsReadyEvent(object sender, EventArgs e) {
// signal
_reply?.TrySetResult();
}
Related
This question already has answers here:
Execute task in background in WPF application
(3 answers)
Closed 2 years ago.
I am monitoring devices, using the example code below, and all works fine. The question is about how to handle each process at the same time. Let me explain.
When monitoring devices, we have an event that gets fired whenever a device disconnect. If for example we have to wait for each device to restart, or to reconnect to network, we use a thread.sleep() before starting the next commands. Only issue is if we have a large number of devices, each device will be done one at the time, potentially taking a long time to complete all devices. In this example, with a 10 seconds sleep, only 6 devices can be done every minutes..
How should I go to start 1 separate process for each device, and run it (almost) simultaneously? Or?
monitor.DeviceDisconnected += this.OnDeviceDisconnected;
private void OnDeviceDisconnected(object sender, DeviceDataEventArgs e)
{
......
Thread.Sleep(10000);
......
}
I would assume that the event is raised on the main thread since the UI freezes. As #Clemens mentions, you should avoid using Thread.Sleep since this will block the thread, and this is quite disrupting if done on the main thread.
To use await the method needs to be marked with async. Async methods need to either return a Task, Task<T> or void. It is generally recommended to return a Task since this lets the caller know when the method completes and if any exceptions occurred.
Since it is an event handler the caller would not care when the task completes, but we should take care to not lose any exceptions . So the method signature would look something like this
private async voidOnDeviceDisconnected(object sender, DeviceDataEventArgs e){
{
......
try{
await Task.Delay(TimeSpan.FromSeconds(10));
....
}
catch(Exception e){
// Handle exception
}
}
Partly as an exercise in exploring async, I though I'd try creating a ServiceBrokerWatcher class. The idea is much the same as a FileSystemWatcher - watch a resource and raise an event when something happens. I was hoping to do this with async rather than actually creating a thread, because the nature of the beast means that most of the time it is just waiting on a SQL waitfor (receive ...) statement. This seemed like an ideal use of async.
I have written code which "works", in that when I send a message through broker, the class notices it and fires off the appropriate event. I thought this was super neat.
But I suspect I have gotten something fundamentally wrong somewhere in my understanding of what is going on, because when I try to stop the watcher it doesn't behave as I expect.
First a brief overview of the components, and then the actual code:
I have a stored procedure which issues a waitfor (receive...) and returns a result set to the client when a message is received.
There is a Dictionary<string, EventHandler> which maps message type names (in the result set) to the appropriate event handler. For simplicity I only have the one message type in the example.
The watcher class has an async method which loops "forever" (until cancellation is requested), which contains the execution of the procedure and the raising of the events.
So, what's the problem? Well, I tried hosting my class in a simple winforms application, and when I hit a button to call the StopListening() method (see below), execution isn't cancelled right away as I thought it would be. The line listener?.Wait(10000) will in fact wait for 10 seconds (or however long I set the timeout). If I watch what happens with SQL profiler I can see that the attention event is being sent "straight away", but still the function does not exit.
I have added comments to the code starting with "!" where I suspect I have misunderstood something.
So, main question: Why isn't my ListenAsync method "honoring" my cancellation request?
Additionally, am I right in thinking that this program is (most of the time) consuming only one thread? Have I done anything dangerous?
Code follows, I tried to cut it down as much as I could:
// class members //////////////////////
private readonly SqlConnection sqlConnection;
private CancellationTokenSource cts;
private readonly CancellationToken ct;
private Task listener;
private readonly Dictionary<string, EventHandler> map;
public void StartListening()
{
if (listener == null)
{
cts = new CancellationTokenSource();
ct = cts.Token;
// !I suspect assigning the result of the method to a Task is wrong somehow...
listener = ListenAsync(ct);
}
}
public void StopListening()
{
try
{
cts.Cancel();
listener?.Wait(10000); // !waits the whole 10 seconds for some reason
} catch (Exception) {
// trap the exception sql will raise when execution is cancelled
} finally
{
listener = null;
}
}
private async Task ListenAsync(CancellationToken ct)
{
using (SqlCommand cmd = new SqlCommand("events.dequeue_target", sqlConnection))
using (CancellationTokenRegistration ctr = ct.Register(cmd.Cancel)) // !necessary?
{
cmd.CommandTimeout = 0;
while (!ct.IsCancellationRequested)
{
var events = new List<string>();
using (var rdr = await cmd.ExecuteReaderAsync(ct))
{
while (rdr.Read())
{
events.Add(rdr.GetString(rdr.GetOrdinal("message_type_name")));
}
}
foreach (var handler in events.Join(map, e => e, m => m.Key, (e, m) => m.Value))
{
if (handler != null && !ct.IsCancellationRequested)
{
handler(this, null);
}
}
}
}
}
You don't show how you've bound it to the WinForms app, but if you are using regular void button1click methods, you may be running into this issue.
So your code will run fine in a console app (it does when I try it) but deadlock when called via the UI thread.
I'd suggest changing your controller class to expose async start and stop methods, and call them via e.g.:
private async void btStart_Click(object sender, EventArgs e)
{
await controller.StartListeningAsync();
}
private async void btStop_Click(object sender, EventArgs e)
{
await controller.StopListeningAsync();
}
Peter had the right answer. I was confused for several minutes about what was deadlocking, but then I had my forehead slapping moment. It is the continuation of ListenAsync after the ExecuteReaderAsync is cancelled, because it's just a task, not a thread of its own. That was, after all, the whole point!
Then I wondered... OK, what if I tell the async part of ListenAsync() that it doesn't need the UI thread. I will call ExecuteReaderAsync(ct) with .ConfigureAwait(false)! Aha! Now the class methods don't have to be async anymore, because in StopListening() I can just listener.Wait(10000), the wait will continue the task internally on a different thread, and the consumer is none the wiser. Oh boy, so clever.
But no, I can't do that. Not in a webforms application at least. If I do that then the textbox is not updated. And the reason for that seems clear enough: the guts of ListenAsync invoke an event handler, and that event handler is a function which wants to update text in a textbox - which no doubt has to happen on the UI thread. So it doesn't deadlock, but it also can't update the UI. If I set a breakpoint in the handler which wants to update the UI the line of code is hit, but the UI can't be changed.
So in the end it seems the only solution in this case is indeed to "go async all the way down". Or in this case, up!
I was hoping that I didn't have to do that. The fact that the internals of my Watcher are using async methodologies rather than just spawning a thread is, in my mind, an "implementation detail" that the caller shouldn't have to care about. But a FileSystemWatcher has exactly the same issue (the need to control.Invoke if you want to update a GUI based on a watcher event), so that's not so bad. If I was a consumer that had to choose between using async or using Invoke, I'd choose async!
I am working on a user interface in C#.
When the program is running (this process takes several minutes...), before run, I want a message is displayed and after run, the message will disappear automatically.
My aim is to give an information message like 'Running, please wait' to the user.
I tried the code shown below:
(formMsgWait has only a label 'Running, please wait')
private void btnExit_Click(object sender, EventArgs e)
{
using (formMsgWait fMsgWait = new formMsgExit())
{
fMsgWait.Show();
System.Windows.Forms.Application.DoEvents();
...statement 1
...statement 2
...
}
}
When run to System.Windows.Forms.Application.DoEvents(); the program doesn't run continue, so all of the statements below doesn't do (...statement 1, ...statement 2, ...), formMsgWait doesn't close.
Is there anyway to do that?
Any tips on these will be great help.
You're blocking the current thread. According to http://msdn.microsoft.com/en-us/library/system.windows.forms.application.doevents.aspx
Calling this method causes the current thread to be suspended while
all waiting window messages are processed. If a message causes an
event to be triggered, then other areas of your application code may
execute. This can cause your application to exhibit unexpected
behaviors that are difficult to debug. If you perform operations or
computations that take a long time, it is often preferable to perform
those operations on a new thread. For more information about
asynchronous programming, see Asynchronous Programming Overview.
So that's what you should actually be doing here: start all of your actual work on a separate thread, preferably using async. For example:
public async Task<bool> DoTheWorkAsync()
{
formMsgWait f = new formMsgWait();
f.Show();
bool finished = await Task.Run(() => DoTheWork());
f.Close();
return finished;
}
private bool DoTheWork()
{
... work
return true;
}
Oh. I think you can use Dialog Window. Visit this link to refer:
https://msdn.microsoft.com/en-us/library/c7ykbedk(v=vs.110).aspx
Let's say I need to implement a C# class library that executes a long-running calculation. When some of the values calculated inside the calculation cross a certain threshold, I have to notify the user, nothing more.
In this case, how do I implement events inside a task? Is that even a proper solution? If not, how can I notify the user of things happening inside my task?
Depending on what you desire to have happen when you say "notify the user", there are quite a few options, some more complex than others (e.g. using a messaging library like RabbitMQ).
However, a simple answer to your question would interpret "notify the user" as meaning "notify the caller of my long running process." In that case, you could create an EventHandler in the class that has your async long running process, and simply call that event handler when you want to "notify" your caller.
Code for the class could look like this:
public class LongTask
{
public event EventHandler StillInProgress;
public async Task<bool> LongRunningProcessAsync()
{
await Task.Delay(5000);
OnStillInProgress(new EventArgs());
await Task.Delay(5000);
return true;
}
protected virtual void OnStillInProgress(EventArgs e)
{
StillInProgress?.Invoke(this, e);
}
}
In the code above, the caller of your long running process could also subscribe to your "StillInProgress" event, and get notifications whenever you call it.
Subscribing to event by caller would look something like this:
var longRun = new LongTask();
longRun.StillInProgress += LongRun_StillInProgress;
// Method that will handle the "notification"
private void LongRun_StillInProgress(object sender, EventArgs e)
{
Debug.WriteLine("InProgress");
}
There are other options, but this is perhaps the most straight-forward.
I've read and experimented more with the problem, and seeing that all I need is to run code on the UI thread when certain conditions of the task are met, it's better to go the IProgress route (Peter Bons and Servy's recommendation). Simpler, better and fully supported. The link posted by Peter Bons (authored by Stephen Cleary) is a great writeup on how to do it.
Thanks to everyone for the help.
In app.xaml.cs
private void Application_Closing(object sender, ClosingEventArgs e)
{
A.onEndApp();
}
In class A
async public static void onEndApp()
{
string temp=await doSomething();
//code here
}
I have a problem,when I close an app,and then onEndApp() method is run,
when doSomething() run complete and {//code here} is not run,but if I put A.onEndApp() in
another method it run normaly,for example Application_Launching() method,it will be run
{//code here}
I think when app is running it no problem,but when app is closing it run await complete and then stop,I want to run complete method when I close app in async.
After Application_Closing is executed (synchronously), the OS will terminate the process. There might be a short delay before it does so, and that might be enough for you to finish writing to isolated storage, or it might not in which case you'll end up having corrupted state
I'll just take a direct quote from "Beware the perils of async/await in application lifecycle event handlers (in fact in any event handlers)" on Andy Wigley's blog.
Calling async code from Application_Deactivated or Application_Closing
The guidance here is “don’t'”. If you write your apps carefully, you can be saving changes to persistent data as you go along, so you shouldn’t have anything to do in the application lifecycle events.
If you must, you can try doing something like this:
SomeAsyncMethod().AsTask().Wait()
If the operation completes within the timeout period AND it doesn’t deadlock due to needing
to pump the UI thread, it will work… but don’t count on it.
Try this:
private void Application_Closing(object sender, ClosingEventArgs e)
{
var task = A.onEndApp();
task.Wait();
}
async public static Task onEndApp()
{
string temp = await doSomething();
//code here
}
Otherwise, you're effectively just spinning off a thread that will never get a chance to run to completion, because the application is closing out from underneath it, and the thread will get terminated before it can run.