How to exit an async method early that wraps an awaitable method? - c#

My class is wrapping TcpListener and I'm trying to update it to use modern async language.
It throws up the question what to do if a method should exit early. If this is the old code:
public void Connect()
{
if (tcp != null)
tcp.Connect(config.IPAddress, config.Port);
}
Then I start with something like this:
public Task ConnectAsync()
{
if (tcp != null)
{ ???? }
return tcp.ConnectAsync(config.IPAddress, config.Port);
}
But in the condition tcp==null what should I return? Is there some "null awaitable"?
I also am unsure if my method should be designated async or not in this case where I am wrapping another method - I am sure I do not want to await the called method. This compiles but I do not think it is right:
public async Task ConnectAsync()
{
if (tcp != null)
return;
await tcp.ConnectAsync(config.IPAddress, config.Port);
}

You can return a completed task:
public Task ConnectAsync()
{
if (tcp == null)
{
return Task.CompletedTask;
}
return tcp.ConnectAsync(config.IPAddress, config.Port);
}
Task.CompletedTask is also a singleton instance, so there is no additional overhead if this method is called regularly.
You could also write your method as async and return nothing at all:
public async Task ConnectAsync()
{
if (tcp != null)
{
await tcp.ConnectAsync(config.IPAddress, config.Port);
}
}

Related

Is it problematic to start a Task in the constructor?

In the Call asynchronous method in constructor? question is no answer that, starts the async Operation in the constructor and store the Task in a member and an awaits it before using the resource:
public class DeviceAccess
{
private readonly Task<Container> containerTask;
public DeviceAccess(Database database)
{
containerTask = GetContainer(database);
}
private async Task<Container> GetContainer(Database database)
{
var conatinerResponse = await database.CreateContainerIfNotExistsAsync("Device");
return conatinerResponse.Container;
}
public async Task<Device> GetDevice(string deviceId)
{
var container = await containerTask;
return await doSomething(container);
}
}
In my case every Operation needs the resource, so I see no advantage to use some lazy loading.
Is it valid to start a async Operation in a constructor or can result this into problems?
The biggest problem I can see here is that [Value]Task[<T>] is an API that enables async, not a promise to be async; just because CreateContainerIfNotExistsAsync is named *Async and returns Task<T> - that doesn't actually mean it is async - it could run synchronously and return a result via Task.FromResult (aka "async over sync"). If you're not concerned about that problem, then fine I guess. But I wonder whether an OpenAsync() method that you call after construction would be more appropriate, i.e.
public class DeviceAccess
{
private Container _container;
public DeviceAccess() {}
public async ValueTask OpenAsync(Database database) {
if (_container == null)
_container = await GetContainerAsync(database);
}
public async Task<Device> GetDeviceAsync(string deviceId)
{
var container = _container ?? throw new InvalidOperationException("not open");
return await doSomething(container); // might be able to inline the "await" here
}
}

Prevent deadlock by running a Task synchronously - Windows Service

I read that sometimes that calling directly a Task can lead to a deadlock of the main thread.
Here's my async method:
public async Task<List<JobsWithSchedules>> fillJobsAsync()
{
IOlapJobAccess jobAccess = new OlapJobAccess(_proxy, CentralPointPath);
List<OlapJob> jobs = await jobAccess.GetAllJobsAsync();
List<JobsWithSchedules> quartzJobs = null;
if (jobs != null)
{
quartzJobs = fillQuartzJobs(jobs);
}
return quartzJobs;
}
I tried a lot of ways to run this task in a sync function. Here's some examples:
public void syncFoo1()
{
var fillJobsTask = fillJobsAsync().ContinueWith((task) =>
{
if (task.Status == TaskStatus.RanToCompletion && task.Result != null)
{
List<JobsWithSchedules> quartzJobs = task.Result;
//...
}
else
{
//...
}
});
fillJobsTask.Wait();
}
public void syncFoo2()
{
Task.Run(() => fillJobsAsync()).ContinueWith((task) =>
{
if (task.Status == TaskStatus.RanToCompletion && task.Result != null)
{
List<JobsWithSchedules> quartzJobs = task.Result;
//...
}
else
{
//...
}
});
}
I want to know which is better solution to run the async method synchronously in the syncFoo() without causing deadlocks. Should I do it like in syncFoo2()?
PS: syncFoo() is called from a a windows service onStart() and onStop().
Since it's in a Windows service, the task should never run synchronously as Stephen suggested. I changed it to be async and it's working fine.

Best way to handle null task inside async method? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
What is the best way to handle a null task inside an async method?
public class MyClass
{
private readonly Task task;
public MyClass(Task task) { this.task = task; }
public async Task Execute()
{
if (task == null)
{
await Task.Yield(); /* Is this the best way? */
return;
}
await task;
}
}
You don't need to handle null tasks. Simply null check it:
public async Task Execute()
{
if (task != null)
{
await task;
}
}
Or even better, simply return the task since you're not adding anything after the await:
public Task Execute()
{
return task;
}
If you want to return a completed task instead of null you can use Task.FromResult:
public Task Execute()
{
return task ?? Task.FromResult(false);
}
Most asynchronous code is cleaner if tasks are never null. Instead of a null task, use Task.FromResult(0) or some such construct.
public class MyClass
{
private readonly Task task;
public MyClass(Task task) { this.task = task ?? Task.FromResult(0); }
public async Task ExecuteAsync()
{
await task;
}
}
Or, if that's really all your ExecuteAsync is doing:
public Task ExecuteAsync()
{
return task;
}
Note that the task is already running when the constructor is called, which makes the method name ExecuteAsync a misnomer. If you want the task to start when ExecuteAsync is called, then what you really want to store is a Func<Task>:
public class MyClass
{
private readonly Func<Task> func;
public MyClass(Func<Task> func) { this.func = func ?? () => Task.FromResult(0); }
public async Task ExecuteAsync()
{
await func();
}
}
What is the best way to handle a null task inside an async method?
Best practices typically include not allowing null parameters (unless the business rules require them).
public class MyClass
{
private readonly Task _task;
public MyClass(Task task)
{
if (task == null)
{
throw new ArgumentNullException("task");
}
this._task = task;
}
public async Task Execute()
{
await this._task;
}
}

Different behavior of exception with async method

Supposed you have 2 async method define as bellow:
public async Task<TResult> SomeMethod1()
{
throw new Exception();
}
public async Task<TResult> SomeMethod2()
{
await Task.Delay(50);
throw new Exception();
}
Now if you await on those 2 methods the behavior will be pretty much the same. But if you are getting the task the behavior is different.
If I want to cache the result of such a computation but only when the task run to completion.
I have to take care of the 2 situation:
First Situation:
public Task<TResult> CachingThis1(Func<Task<TResult>> doSomthing1)
{
try
{
var futur = doSomthing1()
futur.ContinueWith(
t =>
{
// ... Add To my cache
},
TaskContinuationOptions.NotOnFaulted);
}
catch ()
{
// ... Remove from the pending cache
throw;
}
}
Second Situation
public Task<TResult> CachingThis2(Func<Task<TResult>> doSomthing)
{
var futur = SomeMethod2();
futur.ContinueWith(
t =>
{
// ... Add To my cache
},
TaskContinuationOptions.NotOnFaulted);
futur.ContinueWith(
t =>
{
// ... Remove from the pending cache
},
TaskContinuationOptions.OnlyOnFaulted);
}
Now I pass to my caching system the method that will execute the computation to cache.
cachingSystem.CachingThis1(SomeMethod1);
cachingSystem.CachingThis2(SomeMethod2);
Clearly I need to duplicate code in the "ConinueWith on faulted" and the catch block.
Do you know if there is a way to make the exception behave the same whether it is before or after an await?
There's no difference in the exception handling required for both SomeMethod1 and SomeMethod2. They run exactly the same way and the exception would be stored in the returned task.
This can easily be seen in this example;
static void Main(string[] args)
{
try
{
var task = SomeMethod1();
}
catch
{
// Unreachable code
}
}
public static async Task SomeMethod1()
{
throw new Exception();
}
No exception would be handled in this case since the returned task is not awaited.
There is however a distinction between a simple Task-returning method and an async method:
public static Task TaskReturning()
{
throw new Exception();
return Task.Delay(1000);
}
public static async Task Async()
{
throw new Exception();
await Task.Delay(1000);
}
You can avoid code duplication by simply having an async wrapper method that both invokes the method and awaits the returned task inside a single try-catch block:
public static async Task HandleAsync()
{
try
{
await TaskReturning();
// Add to cache.
}
catch
{
// handle exception from both the synchronous and asynchronous parts.
}
}
In addition to what I3arnon said in his answer, in case you ContinueWith on async method without the TaskContinuationOptions you specify, exception captured by the Task parameter you receive in the continuation handler can be handled in the following way:
SomeMethod1().ContinueWith(ProcessResult);
SomeMethod2().ContinueWith(ProcessResult);
With ProcessResult handler which looks like:
private void ProcessResult<TResult>(Task<TResult> task)
{
if (task.IsFaulted)
{
//remove from cahe
}
else if (task.IsCompleted)
{
//add to cache
}
}

What is correct way to combine long-running tasks with async / await pattern?

I have a "High-Precision" timer class that I need to be able to be start, stop & pause / resume. To do this, I'm tying together a couple of different examples I found on the internet, but I'm not sure if I'm using Tasks with asnyc / await correctly.
Here is my relevant code:
//based on http://haukcode.wordpress.com/2013/01/29/high-precision-timer-in-netc/
public class HighPrecisionTimer : IDisposable
{
Task _task;
CancellationTokenSource _cancelSource;
//based on http://blogs.msdn.com/b/pfxteam/archive/2013/01/13/cooperatively-pausing-async-methods.aspx
PauseTokenSource _pauseSource;
Stopwatch _watch;
Stopwatch Watch { get { return _watch ?? (_watch = Stopwatch.StartNew()); } }
public bool IsPaused
{
get { return _pauseSource != null && _pauseSource.IsPaused; }
private set
{
if (value)
{
_pauseSource = new PauseTokenSource();
}
else
{
_pauseSource.IsPaused = false;
}
}
}
public bool IsRunning { get { return !IsPaused && _task != null && _task.Status == TaskStatus.Running; } }
public void Start()
{
if (IsPaused)
{
IsPaused = false;
}
else if (!IsRunning)
{
_cancelSource = new CancellationTokenSource();
_task = new Task(ExecuteAsync, _cancelSource.Token, TaskCreationOptions.LongRunning);
_task.Start();
}
}
public void Stop()
{
if (_cancelSource != null)
{
_cancelSource.Cancel();
}
}
public void Pause()
{
if (!IsPaused)
{
if (_watch != null)
{
_watch.Stop();
}
}
IsPaused = !IsPaused;
}
async void ExecuteAsync()
{
while (!_cancelSource.IsCancellationRequested)
{
if (_pauseSource != null && _pauseSource.IsPaused)
{
await _pauseSource.Token.WaitWhilePausedAsync();
}
// DO CUSTOM TIMER STUFF...
}
if (_watch != null)
{
_watch.Stop();
_watch = null;
}
_cancelSource = null;
_pauseSource = null;
}
public void Dispose()
{
if (IsRunning)
{
_cancelSource.Cancel();
}
}
}
Can anyone please take a look and provide me some pointers on whether I'm doing this correctly?
UPDATE
I have tried modifying my code per Noseratio's comments below, but I still cannot figure out the syntax. Every attempt to pass the ExecuteAsync() method to either TaskFactory.StartNew or Task.Run, results in a compilation error like the following:
"The call is ambiguous between the following methods or properties: TaskFactory.StartNew(Action, CancellationToken...) and TaskFactory.StartNew<Task>(Func<Task>, CancellationToken...)".
Finally, is there a way to specify the LongRunning TaskCreationOption without having to provide a TaskScheduler?
async **Task** ExecuteAsync()
{
while (!_cancelSource.IsCancellationRequested)
{
if (_pauseSource != null && _pauseSource.IsPaused)
{
await _pauseSource.Token.WaitWhilePausedAsync();
}
//...
}
}
public void Start()
{
//_task = Task.Factory.StartNew(ExecuteAsync, _cancelSource.Token, TaskCreationOptions.LongRunning, null);
//_task = Task.Factory.StartNew(ExecuteAsync, _cancelSource.Token);
//_task = Task.Run(ExecuteAsync, _cancelSource.Token);
}
UPDATE 2
I think I've narrowed this down, but still not sure about the correct syntax. Would this be the right way to create the task so that the consumer / calling code continues on, with the task spinning-up and starting on a new asynchronous thread?
_task = Task.Run(async () => await ExecuteAsync, _cancelSource.Token);
//**OR**
_task = Task.Factory.StartNew(async () => await ExecuteAsync, _cancelSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
Here are some points:
async void methods are only good for asynchronous event handlers (more info). Your async void ExecuteAsync() returns instantly (as soon as the code flow reaches await _pauseSource inside it). Essentially, your _task is in the completed state after that, while the rest of ExecuteAsync will be executed unobserved (because it's void). It may even not continue executing at all, depending on when your main thread (and thus, the process) terminates.
Given that, you should make it async Task ExecuteAsync(), and use Task.Run or Task.Factory.StartNew instead of new Task to start it. Because you want your task's action method be async, you'd be dealing with nested tasks here, i.e. Task<Task>, which Task.Run would automatically unwrap for you. More info can be found here and here.
PauseTokenSource takes the following approach (by design, AFAIU): the consumer side of the code (the one which calls Pause) actually only requests a pause, but doesn't synchronize on it. It will continue executing after Pause, even though the producer side may not have reached the awaiting state yet, i.e. await _pauseSource.Token.WaitWhilePausedAsync(). This may be ok for your app logic, but you should be aware of it. More info here.
[UPDATE] Below is the correct syntax for using Factory.StartNew. Note Task<Task> and task.Unwrap. Also note _task.Wait() in Stop, it's there to make sure the task has completed when Stop returns (in a way similar to Thread.Join). Also, TaskScheduler.Default is used to instruct Factory.StartNew to use the thread pool scheduler. This is important if your create your HighPrecisionTimer object from inside another task, which in turn was created on a thread with non-default synchronization context, e.g. a UI thread (more info here and here).
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
public class HighPrecisionTimer
{
Task _task;
CancellationTokenSource _cancelSource;
public void Start()
{
_cancelSource = new CancellationTokenSource();
Task<Task> task = Task.Factory.StartNew(
function: ExecuteAsync,
cancellationToken: _cancelSource.Token,
creationOptions: TaskCreationOptions.LongRunning,
scheduler: TaskScheduler.Default);
_task = task.Unwrap();
}
public void Stop()
{
_cancelSource.Cancel(); // request the cancellation
_task.Wait(); // wait for the task to complete
}
async Task ExecuteAsync()
{
Console.WriteLine("Enter ExecuteAsync");
while (!_cancelSource.IsCancellationRequested)
{
await Task.Delay(42); // for testing
// DO CUSTOM TIMER STUFF...
}
Console.WriteLine("Exit ExecuteAsync");
}
}
class Program
{
public static void Main()
{
var highPrecisionTimer = new HighPrecisionTimer();
Console.WriteLine("Start timer");
highPrecisionTimer.Start();
Thread.Sleep(2000);
Console.WriteLine("Stop timer");
highPrecisionTimer.Stop();
Console.WriteLine("Press Enter to exit...");
Console.ReadLine();
}
}
}
I'm adding code for running long running task (infinite with cancelation) with internal sub tasks:
Task StartLoop(CancellationToken cancellationToken)
{
return Task.Factory.StartNew(async () => {
while (true)
{
if (cancellationToken.IsCancellationRequested)
break;
await _taskRunner.Handle(cancellationToken);
await Task.Delay(TimeSpan.FromMilliseconds(100), cancellationToken);
}
},
cancellationToken,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
}

Categories

Resources