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.
Related
I know this is not using the proper form, but can someone tell me why the first code snippet works and the 2nd causes a deadlock?
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = DownloadStringV3("https://www.google.com");
}
public string DownloadStringV3(String url)
{
var resp = new HttpClient().GetAsync(url).Result;
return resp.Content.ReadAsStringAsync().Result;
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = DownloadStringV3("https://www.google.com").Result;
}
public async Task<string> DownloadStringV3(String url)
{
var resp = await new HttpClient().GetAsync(url);
return await resp.Content.ReadAsStringAsync();
}
Different synchronization contexts? This is using .NET 4.8
Every time you await something, you say, I am temporary done here, give some other thread a chance to execute and then continue from here, when the async task is completed.
So in your case await new HttpClient().GetAsync(url) switches the the ui thread and waiting for DownloadStringV3("https://www.google.com").Result be done, which will never be the case, because we do never execute return await resp.Content.ReadAsStringAsync().
Thats why you should do async all the way to the root and never call Result.
The first code works, because you block on the UI thread and don't try to switch to another thread, while waiting for it.
To make the second one working you need to:
private async void button1_Click(object sender, EventArgs e)
{
// make with try catch sure, you catch any error, async void is special. Better is async Task
textBox1.Text = await DownloadStringV3("https://www.google.com");
}
public async Task<string> DownloadStringV3(String url)
{
var resp = await new HttpClient().GetAsync(url);
return await resp.Content.ReadAsStringAsync();
}
I'm trying to use the new .NET 6 Task.WaitAsync(CancellationToken) API.
What I'd like to accomplish is to cancel the waiting for a task, while still being capable of trying to cancel the task itself (it calls an async library and I cannot be sure it will observe the cancellationToken I passed in, or at least not in a timely manner) and avoid to swallow any possible exception it could throw.
So, for example, let's say I want to call an async method:
private async Task DoSomethingAsync(CancellationToken cancellationToken)
{
//do something before the call
await Library.DoSomethingAsync(cancellationToken); // Let's pretend
// this is a call to a libary that accepts a cancellationToken,
// but I cannot be 100% sure it will be observed
}
from a button click event handler:
private async void button1_Click(object sender, EventArgs e)
{
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var tsk = DoSomethingAsync(cts.Token);
try
{
await tsk.WaitAsync(cts.Token);
}
catch (Exception ex) when (ex is OperationCanceledException)
{
tsk.Forget();
}
}
Now I'm sure the await will last 5 seconds max, but when the OperationCanceledException is caught the task could still be running and I don't want to swallow any of the exceptions that it could throw.
So what can I do now if I don't want to await it?
I thought using a FireAndForget extension method like this inside the catch block:
public static async void Forget(this Task task)
{
try
{
await task.ConfigureAwait(false);
}
catch (Exception)
{
throw;
}
}
Is this an acceptable pattern, or should I just trust the library and hope it will sooner or later be canceled anyway?
And what if it will never do so, will the Forget method await forever?
You could combine the WaitAsync and Forget functionality in a single extension method like the one below:
public async static Task WaitAsyncAndThenOnErrorCrash(this Task task,
CancellationToken cancellationToken)
{
Task waitTask = task.WaitAsync(cancellationToken);
try { await waitTask; }
catch when (waitTask.IsCanceled) { OnErrorCrash(task); throw; }
static async void OnErrorCrash(Task task)
{
try { await task.ConfigureAwait(false); }
catch when (task.IsCanceled) { } // Ignore overdue cancellation
}
}
Usage example:
private async void button1_Click(object sender, EventArgs e)
{
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
try
{
await DoSomethingAsync(cts.Token).WaitAsyncAndThenOnErrorCrash(cts.Token);
}
catch (OperationCanceledException) { } // Ignore
}
In case the DoSomethingAsync completes with error either before or after the cancellation, the application will crash with a popup saying "Unhandled exception has occurred in your application". The user will have the option to continue running the app, by clicking the "Continue" button:
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
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.
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.