How to Call an Async Method from within ElapsedEventHandler - c#

I'm going to use a Windows Service to send Telegram Messages periodically (Every two minutes). My Windows Service starts fine and after 2 minutes it is stopped. I checked my code and find out it is because of async. How can I solve the problem?
protected override void OnStart(string[] args)
{
//< I declared a System.Timers.Timer to send new Telegram messages.
aTimer = new System.Timers.Timer(120000); // 2 minutes
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
aTimer.Enabled = true;
GC.KeepAlive(aTimer);
//>
}
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
SendNewMessages();
}
async static void SendNewMessages()
{
MyDataContext myDB = new MyDataContext();
var newMessages = myDB.TelegramMessages.Where(tm => tm.Status != "New Message");
foreach (TelegramMessage newMessage in newMessages)
{
try
{
var store = new FileSessionStore();
var client = new TelegramClient(store, "MySession");
await client.Connect();
var res = await client.ImportContactByPhoneNumber(newMessage.ReceiverPhoneNumber);
await client.SendMessage(res.Value, newMessage.Message);
newMessage.Status = "Sent";
myDB.SubmitChanges();
}
catch (Exception ex)
{
newMessage.Status = ex.Message;
myDB.SubmitChanges();
}
Thread.Sleep(5000);
}
}

One thing I see directly is that async/await has not been implemented all the way to the eventhandler since "SendNewMessages" returns void. And your eventhandler is not async.
According to MSDN on "Async Return Types (C# and Visual Basic)"
The primary use of the void return type (Sub procedures in Visual Basic) is in event handlers, where a void return type is required. A void return also can be used to override void-returning methods or for methods that perform activities that can be categorized as "fire and forget."
This is most likely an issue in your scenario, so you could try changing your SendNewMessage to this
async static Task SendNewMessages()
And your eventhandler to this
private async static void OnTimedEvent(object source, ElapsedEventArgs e)
{
await SendNewMessages();
}
UPDATED
It would also be a good idea to add some errorhandling code to your "SendNewMessages"-method since if an exception is thrown, your service will quit.
async static Task SendNewMessages()
{
try
{
... Your code here
}
catch(Exception e)
{
... exceptionhandling here
}
}
At the moment you only have exceptionhandling within your foreach, but you do not have any errorhandling (as far as I can see) for your database-code.
if an exception is throw here
MyDataContext myDB = new MyDataContext();
var newMessages = myDB.TelegramMessages.Where(tm => tm.Status != "New Message");
foreach (TelegramMessage newMessage in newMessages)
or here:
newMessage.Status = ex.Message;
myDB.SubmitChanges();
The service would end

Related

How to properly delay when there is no task to wait for

I have a task that is waiting for a property to be set to true (= completed). The way I am receiving that property value change is via EventHandler (System.Diagnostics.Process.OutputDataReceived to be exact - it continuously reads the output of another process until the correct output is provided). However checking for the property all the time feels somewhat inefficiencient. I have tried adding a small delay of one tick because I believe I can allow myself such a wait if that would save CPU time, but I read .NET struggles with fractional milliseconds. Can I improve this code?
private ConcurrentBag<string> _allMessages = new ConcurrentBag<string>();
public OutputRetriever()
{
var process = new System.Diagnostics.Process();
...
process.OutputDataReceived += OutputDataReceived;
process.Start();
}
public async Task<string[]> GetAllOutput()
{
while (!IsCompleted)
{
// how to properly wait here?
// await Task.Delay(TimeSpan.FromTicks(1)); // is this ok?
}
return _allMessages.ToArray();
}
private void ConsoleDataReceived(object sender, DataReceivedEventArgs e)
{
_allMessages.Add(e?.Data);
if (e?.Data == "success")
{
IsCompleted = true;
}
}
The timers in Windows have a resolution of approx. 16 ms, so any delay below 16 ms cannot be precisely achieved. This applies to any timer - the .NET timers are just wrappers for Windows native timers.
Instead of busy-waiting in a loop, create a custom TaskCompletionSource<T> and return a Task that can be awaited.
class OutputRetriever
{
private readonly ConcurrentBag<string> _allMessages = new ConcurrentBag<string>();
private readonly TaskCompletionSource<string[]> _taskSource
= new TaskCompletionSource<string[]>();
// Note: this method is not async anymore
public Task<string[]> GetAllOutput()
{
// We just return a task that can be awaited
return _taskSource.Task;
}
void ConsoleDataReceived(object sender, DataReceivedEventArgs e)
{
_allMessages.Add(e?.Data);
if (e?.Data == "success")
{
// Here we notify that the task is completed by setting the result
_taskSource.SetResult(_allMessages.ToArray());
}
}
}
Now the clients can simply await the results as usual:
var receiver = new OutputReceiver();
string[] messages = await receiver.GetAllOutput();

Async has a wrong return type

I am trying to implement a spinner in my wpf. If the data are being fetched the spinner should be shown but as soon as all data are fetched, then the spinner should be hidden. I know I have to use async and await for this to work but my code below doesn't work.
private async Task onGetFiles(object sender, RoutedEventArgs e)
{
if (txtIpAddress.Text.Contains("IP Address"))
{
MessageBox.Show("Ip Address is invalid");
return;
} else if (string.IsNullOrEmpty(dpDate.Text))
{
MessageBox.Show("Date is invalid");
return;
}
var date = dpDate.Text;
var splitDate = date.Split('/');
int month = Convert.ToInt32(splitDate[1]);
int day = Convert.ToInt32(splitDate[0]);
var year = splitDate[2];
var filePath = $#"\\{txtIpAddress.Text}\i\Hardware Interfacing\{year}\{month}\{day}\PeripheralLogsDq.txt";
using (new ImpersonateUser("username", "", "password"))
{
IsWaveActive = true;
await Task.Run(() => LoadLogs(rbQueue, File.ReadAllText(filePath)));
await Task.Run(() => LoadLogs(rbQueue, File.ReadAllText(filePath)));
IsWaveActive = false;
}
}
private bool LoadLogs(RichTextBox rtb, string msg)
{
try
{
FlowDocument flowDocument = new FlowDocument();
Paragraph paragraph = new Paragraph();
paragraph.Inlines.Add(new Run(msg));
flowDocument.Blocks.Add(paragraph);
rtb.Document = flowDocument;
return true;
}
catch
{
return false;
}
}
My error is
Error CS0407 'Task MainWindow.onGetFiles(object, RoutedEventArgs)' has the wrong return type MenuAnimation C:\Users\Rodne\OneDrive\Desktop\AnimatedMenu1-master\MenuAnimation\MainWindow.xaml 188 Active
I get this error when I try to compile so I am not able to run it.
Unfortunately, async event handlers (which onGetFiles appears to be) often have to be declared async void1 rather than async Task.
This is unfortunate since it means there's no way to determine when they've finished doing their job using the usual Task based infrastructure.
However, if IsWaveActive is how your achieving the "show spinner until done" function, that should work just fine.
1Because the delegate type for the event handler, being sensible, is declared as void returning rather than having any type of return value.
Do you await the async method onGetFiles? You'd better rename the method to onGetFilesAsync so you don't forget to await it!

PageAsyncTask Timeout - How to stop execution of async code

I'm trying to implement Async calls in a classic ASP.NET web page.
I'm following the instructions I've found here and I can get it to call asynchronously but I don't know how to cancel the execution of the async method when the request times out (which I can easily do by pausing execution of my code at a breakpoint).
My code so far is as follows:
protected void Button_Click(object sender, EventArgs e)
{
PageAsyncTask task = new PageAsyncTask(OnBegin, OnEnd, OnTimeout, null);
Page.RegisterAsyncTask(task);
Page.AsyncTimeout = new TimeSpan(0, 0, 10);
Page.ExecuteRegisteredAsyncTasks();
}
protected delegate void AsyncTaskDelegate();
private AsyncTaskDelegate _dlgt;
public IAsyncResult OnBegin(object sender, EventArgs e, AsyncCallback cb, object extraData)
{
_dlgt = new AsyncTaskDelegate(ExecuteAsyncTask);
IAsyncResult result = _dlgt.BeginInvoke(cb, extraData);
return result;
}
public void OnEnd(IAsyncResult ar)
{
_dlgt.EndInvoke(ar);
}
private void ExecuteAsyncTask()
{
MyObject obj = new MyObject();
var task = obj.CompleteAsync();
if (obj.Complete())
{
LabelResult.Text = "Success";
}
else
{
LabelResult.Text = "Failed";
}
}
public void OnTimeout(IAsyncResult ar)
{
_dlgt.EndInvoke(ar);//this does not work!
}
The main issue I have is that the code can "hang" at a certain point in the async method as I am calling an outside service and it can stop responding under certain situations.
Even if I try to EndInvoke the request the async code still runs.
Any help would be appreciated.
OK so I I've worked it out. It has to do with cancellation tokens.
First I created a CancellationTokenSource:
private System.Threading.CancellationTokenSource tokenSource = new System.Threading.CancellationTokenSource();
Then I pass this into a Task factory to start the async task:
var task = System.Threading.Tasks.Task.Factory.StartNew(() => myObject.AsyncMethod(Request.QueryString, tokenSource.Token), tokenSource.Token);
And finally when the timeout occurs I simple do this:
public void OnTimeout(IAsyncResult ar)
{
tokenSource.Cancel();
tokenSource.Token.ThrowIfCancellationRequested();
}
VoilĂ !
This causes an exception which you then need to catch but at least this stops execution of any code.

How do you cancel an async operation in C++/CX from C#

I'm trying to cancel an operation that is written in C++/CX from C#. Despite the fact I wrote both pieces of code I can't get the operation to cancel properly when awaiting it from the C# side. Here's an example:
From C#:
var tcs = new CancellationTokenSource();
tcs.Cancel();
var class1 = new MyClass();
try
{
var asyncOp = await class1.DoSomeTaskAsync().AsTask(tcs.Token);
}
catch (OperationCanceledException oce)
{
//I want to get here
Handle(oce);
}
From C++:
IAsyncOperation<bool>^ MyClass::DoSomeTaskAsync(){
return concurrency::create_async([](concurrency::cancellation_token ct) {
task<bool> my_task([]() {
doSomething1();
if (concurrency::is_task_cancellation_requested())
{
concurrency::cancel_current_task();
}
doSomething2();
return false;
}, ct);
return my_task;
});
}
The problem seems to be that passing in the token into the AsTask extension method does nothing when calling a task across the ABI. When debugging the C++ side both the ct and the is_task_cancellation_requested() function indicate that a cancellation has not been requested.
Try this. In your C++ Windows Runtime Component do something that takes a long time, for example, concurrency::wait(2000) will sleep the thread for two seconds (do not do this in a real app).
#include <ppltasks.h>
using namespace Windows::Foundation;
IAsyncOperation<bool>^ MyClass::DoSomeTaskAsync()
{
return concurrency::create_async([=](concurrency::cancellation_token token)
{
// Do something.
concurrency::wait(2000);
if (concurrency::is_task_cancellation_requested())
{
concurrency::cancel_current_task();
}
// Do something else.
concurrency::wait(2000);
return true;
});
}
Then, in your C# Windows Store App create two buttons:
<Button x:Name="DoButton" Click="DoButton_Click">Do</Button>
<Button x:Name="CancelButton" Click="CancelButton_Click">Cancel</Button>
Call your component's asynchronous method in the Do button.
private System.Threading.CancellationTokenSource cts;
private async void DoButton_Click(object sender, RoutedEventArgs e)
{
cts = new System.Threading.CancellationTokenSource();
var class1 = new MyClass();
try
{
var asyncOp = await class1.DoSomeTaskAsync().AsTask(cts.Token);
System.Diagnostics.Debug.WriteLine(asyncOp);
}
catch (OperationCanceledException oce)
{
// I want to get here.
System.Diagnostics.Debug.WriteLine(oce);
}
}
And cancel the operation in the Cancel button.
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
cts.Cancel();
cts = null;
}
Run the app, click Do button and within two seconds click Cancel button. You should get a System.Threading.OperationCanceledException.

Message Web Socket get exception a method was called at an unexpected time

Hi Im using MessageWebSocket in my windows store app but when I call method mws() I get:
A method was called at an unexpected time
I need call this method from another class because I want use the same MessageWebSocket for one connection and that is reason why is my method static.
private static MessageWebSocket messageWebSocket = null;
public static MessageWebSocket mws()
{
if (messageWebSocket == null)
messageWebSocket = new MessageWebSocket();
return messageWebSocket;
}
private async void websocketRequestRegisterDevice(object sender, TappedRoutedEventArgs e)
{
...
cos.WriteRawBytes(new byte[] { 7, 1, 0, 0 });
req.WriteTo(cos);
mws();
mws().Control.MessageType = SocketMessageType.Binary;
mws().MessageReceived += websocketResponseRegisterDevice;
await mws().ConnectAsync(server);
messageWriter = new DataWriter(mws().OutputStream);
messageWriter.WriteBytes(buff);
await messageWriter.StoreAsync();
}
I call it in async method but if I want use await write me for example message type is not awaitable.
You're probably calling ConnectAsync on a connected socket.
So you'd want something like this:
private static Task<MessageWebSocket> messageWebSocket = null;
public static Task<MessageWebSocket> mws()
{
if (messageWebSocket == null)
messageWebSocket = CreateMessageWebSocket();
return messageWebSocket;
}
private static async Task<MessageWebSocket> CreateMessageWebSocket()
{
var ret = new MesesageWebSocket();
await ret.ConnectAsync();
return ret;
}
private async Task websocketRequestRegisterDevice(object sender, TappedRoutedEventArgs e)
{
...
cos.WriteRawBytes(new byte[] { 7, 1, 0, 0 });
req.WriteTo(cos);
var s = await mws();
s.Control.MessageType = SocketMessageType.Binary;
s.MessageReceived += websocketResponseRegisterDevice;
messageWriter = new DataWriter(s.OutputStream);
messageWriter.WriteBytes(buff);
await messageWriter.StoreAsync();
}
I also changed your async void method to async Task, since you should avoid async void. However, note a few things that are still not optimal:
There's better ways to handle connection sharing than static fields.
Your various methods are still installing multiple handlers for MessageReceived, so they will probably get confused.

Categories