Looking for help after searching has not produced a good suggestion.
I always avoid having async void methods in code. I don't use event handlers. Sometimes a vendor or library gives you no choice, and their methods are implemented as async void.
If my method itself returns Task, but i have no choice but to call a 3rd party library method with async void, is there a way to safely wrap their method in such a way that I can keep my code free of the async void dangers, as listed here about terminating my process?
StackOverflow - why is async void bad
An example of my concern is as follows:
3rd party library method looks like this
public async void GetSomethingFromAService()
{
/// their implementation, and somewhere along the way it throws an exception, in this async void method --- yuck for me
}
My method say on a service controller:
public async Task<bool> MyMethod()
{
await ThirdPartyLibrary.GetSomethingFromAService();
return await Task.FromResult(true);
}
My method is fine except the 3rd party library is async void and throws an exception. My app is going to die. I don't want it to because my code is well written an not async void. But I can't control their code. Can i wrap the call to their async void method in such a way to protect my code from dying?
It's tricky and it might not work for all scenarios, but it may be possible to track the life-time of an async void method, by starting its execution on a custom synchronization context. In this case, SynchronizationContext.OperationStarted / SynchronizationContext.OperationCompleted will be called upon start and end of the asynchronous void method, correspondingly.
In case an exception is thrown inside an async void method, it will be caught and re-thrown via SynchronizationContext.Post. Thus, it's also possible to collect all exceptions.
Below is an a complete console app example illustrating this approach, loosely based on Stephen Toub's AsyncPump (warning: only slightly tested):
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsyncVoidTest
{
class Program
{
static async void GetSomethingFromAService()
{
await Task.Delay(2000);
throw new InvalidOperationException(nameof(GetSomethingFromAService));
}
static async Task<int> MyMethodAsync()
{
// call an ill-designed 3rd party async void method
// and await its completion
var pump = new PumpingContext();
var startingTask = pump.Run(GetSomethingFromAService);
await Task.WhenAll(startingTask, pump.CompletionTask);
return 42;
}
static async Task Main(string[] args)
{
try
{
await MyMethodAsync();
}
catch (Exception ex)
{
// this will catch the exception thrown from GetSomethingFromAService
Console.WriteLine(ex);
}
}
}
/// <summary>
/// PumpingContext, based on Stephen Toub's AsyncPump
/// https://blogs.msdn.com/b/pfxteam/archive/2012/02/02/await-synchronizationcontext-and-console-apps-part-3.aspx
/// https://stackoverflow.com/q/49921403/1768303
/// </summary>
internal class PumpingContext : SynchronizationContext
{
private int _pendingOps = 0;
private readonly BlockingCollection<ValueTuple<SendOrPostCallback, object>> _callbacks =
new BlockingCollection<ValueTuple<SendOrPostCallback, object>>();
private readonly List<Exception> _exceptions = new List<Exception>();
private TaskScheduler TaskScheduler { get; }
public Task CompletionTask { get; }
public PumpingContext(CancellationToken token = default(CancellationToken))
{
var taskSchedulerTcs = new TaskCompletionSource<TaskScheduler>();
this.CompletionTask = Task.Run(() =>
{
SynchronizationContext.SetSynchronizationContext(this);
taskSchedulerTcs.SetResult(TaskScheduler.FromCurrentSynchronizationContext());
try
{
// run a short-lived callback pumping loop on a pool thread
foreach (var callback in _callbacks.GetConsumingEnumerable(token))
{
try
{
callback.Item1.Invoke(callback.Item2);
}
catch (Exception ex)
{
_exceptions.Add(ex);
}
}
}
catch (Exception ex)
{
_exceptions.Add(ex);
}
finally
{
SynchronizationContext.SetSynchronizationContext(null);
}
if (_exceptions.Any())
{
throw new AggregateException(_exceptions);
}
}, token);
this.TaskScheduler = taskSchedulerTcs.Task.GetAwaiter().GetResult();
}
public Task Run(
Action voidFunc,
CancellationToken token = default(CancellationToken))
{
return Task.Factory.StartNew(() =>
{
OperationStarted();
try
{
voidFunc();
}
finally
{
OperationCompleted();
}
}, token, TaskCreationOptions.None, this.TaskScheduler);
}
public Task<TResult> Run<TResult>(
Func<Task<TResult>> taskFunc,
CancellationToken token = default(CancellationToken))
{
return Task.Factory.StartNew<Task<TResult>>(async () =>
{
OperationStarted();
try
{
return await taskFunc();
}
finally
{
OperationCompleted();
}
}, token, TaskCreationOptions.None, this.TaskScheduler).Unwrap();
}
// SynchronizationContext methods
public override SynchronizationContext CreateCopy()
{
return this;
}
public override void OperationStarted()
{
// called when async void method is invoked
Interlocked.Increment(ref _pendingOps);
}
public override void OperationCompleted()
{
// called when async void method completes
if (Interlocked.Decrement(ref _pendingOps) == 0)
{
_callbacks.CompleteAdding();
}
}
public override void Post(SendOrPostCallback d, object state)
{
_callbacks.Add((d, state));
}
public override void Send(SendOrPostCallback d, object state)
{
throw new NotImplementedException(nameof(Send));
}
}
}
Related
I need to execute a kind of LongRunning task after a delay.
Each Task can be cancelled. I prefer TPL with cancellationToken.
Since my task is long running and before starting a task it has to be placed in dictionary I have to use new Task(). But I've faced different behavior - when task is created using new Task() after Cancel() it throws TaskCanceledException whereas a task created with Task.Run doesn't throw an exception.
Generally I need to recognize the difference and not get TaskCanceledException.
It's my code:
internal sealed class Worker : IDisposable
{
private readonly IDictionary<Guid, (Task task, CancellationTokenSource cts)> _tasks =
new Dictionary<Guid, (Task task, CancellationTokenSource cts)>();
public void ExecuteAfter(Action action, TimeSpan waitBeforeExecute, out Guid cancellationId)
{
var cts = new CancellationTokenSource();
var task = new Task(async () =>
{
await Task.Delay(waitBeforeExecute, cts.Token);
action();
}, cts.Token, TaskCreationOptions.LongRunning);
cancellationId = Guid.NewGuid();
_tasks.Add(cancellationId, (task, cts));
task.Start(TaskScheduler.Default);
}
public void ExecuteAfter2(Action action, TimeSpan waitBeforeExecute, out Guid cancellationId)
{
var cts = new CancellationTokenSource();
cancellationId = Guid.NewGuid();
_tasks.Add(cancellationId, (Task.Run(async () =>
{
await Task.Delay(waitBeforeExecute, cts.Token);
action();
}, cts.Token), cts));
}
public void Abort(Guid cancellationId)
{
if (_tasks.TryGetValue(cancellationId, out var value))
{
value.cts.Cancel();
//value.task.Wait();
_tasks.Remove(cancellationId);
Dispose(value.cts);
Dispose(value.task);
}
}
public void Dispose()
{
if (_tasks.Count > 0)
{
foreach (var t in _tasks)
{
Dispose(t.Value.cts);
Dispose(t.Value.task);
}
_tasks.Clear();
}
}
private static void Dispose(IDisposable obj)
{
if (obj == null)
{
return;
}
try
{
obj.Dispose();
}
catch (Exception ex)
{
//Log.Exception(ex);
}
}
}
internal class Program
{
private static void Main(string[] args)
{
Action act = () => Console.WriteLine("......");
Console.WriteLine("Started");
using (var w = new Worker())
{
w.ExecuteAfter(act, TimeSpan.FromMilliseconds(10000), out var id);
//w.ExecuteAfter2(act, TimeSpan.FromMilliseconds(10000), out var id);
Thread.Sleep(3000);
w.Abort(id);
}
Console.WriteLine("Enter to exit");
Console.ReadKey();
}
}
UPD:
This approach also works without exception
public void ExecuteAfter3(Action action, TimeSpan waitBeforeExecute, out Guid cancellationId)
{
var cts = new CancellationTokenSource();
cancellationId = Guid.NewGuid();
_tasks.Add(cancellationId, (Task.Factory.StartNew(async () =>
{
await Task.Delay(waitBeforeExecute, cts.Token);
action();
}, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default), cts)); ;
}
The reason of the inconsistent behavior is fundamentally incorrect usage of an async delegate in the first case. The Task constructors just don't receive Func<Task> and your asynchronous delegate is always interpreted as async void not async Task in case of using with constructor. If an exception is raised in an async Task method it's caught and placed into Task object which isn't true for an async void method, in that case exception just bubbles up out of the method to a synchronization context and goes under category of unhandled exceptions (you can familiarize with details in this Stephen Cleary article). So what happens in case of using constructor: a task which is supposed to initiate asynchronous flow is created and started. Once it reaches point when Task.Delay(...) returns a promise, the task completes and it has no more relationship to anything which happens in Task.Delay continuation (you can easily check in debugger by setting a breakpoint to value.cts.Cancel() that the task object in the _tasks dictionary has status RanToCompletetion while however the task delegate essentially is still running). When a cancellation is requested the exception is raised inside the Task.Delay method and without existence of any promise object is being promoted to app domain.
In case of Task.Run the situation is different because there are overloads of this method which are able to accept Func<Task> or Func<Task<T>> and unwrap the tasks internally in order to return underlying promise instead of wrapped task which ensures proper task object inside the _tasks dictionary and proper error handling.
The third scenario despite the fact that it doesn't throw an exception it is partially correct. Unlike Task.Run, Task.Factory.StartNew doesn't unwrap underlying task to return promise, so task stored in the _tasks is just wrapper task, like in the case with constructor (again you can check its state with debugger). It however is able to understand Func<Task> parameters, so asynchronous delegate has async Task signature which allows at least to handle and store exception in the underlying task. In order to get this underlying task with Task.Factory.StartNew you need to unwrap the task by yourself with Unwrap() extension method.
The Task.Factory.StartNew isn't considered as a beast practice of creating tasks because of certain dangers related to its application (see there). It however can be used with some caveats if you need to apply specific options like LongRunning which cannot be directly applied with Task.Run.
I don't know why I got down votes here but it's inspired me to update my answer.
UPDATED
My full approach:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp4
{
internal class Program
{
private static void Main(string[] args)
{
using (var delayedWorker = new DelayedWorker())
{
delayedWorker.ProcessWithDelay(() => { Console.WriteLine("100"); }, TimeSpan.FromSeconds(5), out var cancellationId_1);
delayedWorker.ProcessWithDelay(() => { Console.WriteLine("200"); }, TimeSpan.FromSeconds(10), out var cancellationId_2);
delayedWorker.ProcessWithDelay(() => { Console.WriteLine("300"); }, TimeSpan.FromSeconds(15), out var cancellationId_3);
Cancel_3(delayedWorker, cancellationId_3);
Console.ReadKey();
}
}
private static void Cancel_3(DelayedWorker delayedWorker, Guid cancellationId_3)
{
Task.Run(() => { delayedWorker.Abort(cancellationId_3); }).Wait();
}
internal sealed class DelayedWorker : IDisposable
{
private readonly object _locker = new object();
private readonly object _disposeLocker = new object();
private readonly IDictionary<Guid, (Task task, CancellationTokenSource cts)> _tasks = new Dictionary<Guid, (Task task, CancellationTokenSource cts)>();
private bool _disposing;
public void ProcessWithDelay(Action action, TimeSpan waitBeforeExecute, out Guid cancellationId)
{
Console.WriteLine("Creating delayed action...");
CancellationTokenSource tempCts = null;
CancellationTokenSource cts = null;
try
{
var id = cancellationId = Guid.NewGuid();
tempCts = new CancellationTokenSource();
cts = tempCts;
var task = new Task(() => { Process(action, waitBeforeExecute, cts); }, TaskCreationOptions.LongRunning);
_tasks.Add(cancellationId, (task, cts));
tempCts = null;
task.ContinueWith(t =>
{
lock (_disposeLocker)
{
if (!_disposing)
{
TryRemove(id);
}
}
}, TaskContinuationOptions.ExecuteSynchronously);
Console.WriteLine($"Created(cancellationId: {cancellationId})");
task.Start(TaskScheduler.Default);
}
finally
{
if (tempCts != null)
{
tempCts.Dispose();
}
}
}
private void Process(Action action, TimeSpan waitBeforeExecute, CancellationTokenSource cts)
{
Console.WriteLine("Starting delayed action...");
cts.Token.WaitHandle.WaitOne(waitBeforeExecute);
if (cts.Token.IsCancellationRequested)
{
return;
}
lock (_locker)
{
Console.WriteLine("Performing action...");
action();
}
}
public bool Abort(Guid cancellationId)
{
Console.WriteLine($"Aborting(cancellationId: {cancellationId})...");
lock (_locker)
{
if (_tasks.TryGetValue(cancellationId, out var value))
{
if (value.task.IsCompleted)
{
Console.WriteLine("too late");
return false;
}
value.cts.Cancel();
value.task.Wait();
Console.WriteLine("Aborted");
return true;
}
Console.WriteLine("Either too late or wrong cancellation id");
return true;
}
}
private void TryRemove(Guid id)
{
if (_tasks.TryGetValue(id, out var value))
{
Remove(id, value.task, value.cts);
}
}
private void Remove(Guid id, Task task, CancellationTokenSource cts)
{
_tasks.Remove(id);
Dispose(cts);
Dispose(task);
}
public void Dispose()
{
lock (_disposeLocker)
{
_disposing = true;
}
if (_tasks.Count > 0)
{
foreach (var t in _tasks)
{
t.Value.cts.Cancel();
t.Value.task.Wait();
Dispose(t.Value.cts);
Dispose(t.Value.task);
}
_tasks.Clear();
}
}
private static void Dispose(IDisposable obj)
{
if (obj == null)
{
return;
}
try
{
obj.Dispose();
}
catch (Exception ex)
{
//log ex
}
}
}
}
}
What I would like to write is the following:
async void Foo()
{
var result = await GetMyTask().IgnoreCancelAndFailure();
ProcessResult(result);
}
Instead of:
void Foo()
{
GetMyTask().ContinueWith(task => ProcessResult(task.Result),
TaskContinuationOptions.OnlyOnRanToCompletion);
}
However I don't know how to implement the method IgnoreCancelAndFailure, which would have the following signature:
//On cancel or failure this task should simply stop and never complete.
Task<T> IgnoreCancelAndFailure<T>(this Task<T> task)
{
throw new NotImplementedException();
}
If possible, how should I implement IgnoreCancelAndFailure?
You could do something like that, but you need to know what you want the method to return in case of failure, since a return value is expected:
public static async Task<T> IgnoreCancelAndFailure<T>(this Task<T> task)
{
try
{
return await task;
}
catch
{
return ???; // whatever you want to return in this case
}
}
If it's a Task with no result, just leave the catch empty (or perhaps log the exception... swallowed exceptions make for hard debugging)
If you just want to execute ProcessResult only when GetMyTask succeeds, you can do this:
async void Foo()
{
try
{
var result = await GetMyTask();
ProcessResult(result);
}
catch(Exception ex)
{
// handle the exception somehow, or ignore it (not recommended)
}
}
You will never be able to stop your code from continuing expect when killing the thread or process. keep in mind that the await task can be considered a function call that will always have to return a value or throw an exception.
The closest way to shorten your code is creating a wrapper function that uses the ProcessResult method as Action argument.
Something like that:
public static async Task IgnoreCancelAndFailure<T>(this Task<T> task, Action<T> resultProcessor)
{
task.ContinueWith(t => resultProcessor(t.Result),
TaskContinuationOptions.OnlyOnRanToCompletion);
}
async void Foo()
{
GetMyTask().IgnoreCancelAndFailure(ProcessResult);
}
I think I found the answer. The following seems to do the trick. It uses the awaitable pattern. Could you guys confirm that this isn't evil?
class User
{
async void Foo()
{
var result = await GetMyTask().IgnoreCancelAndFailure();
ProcessResult(result);
}
}
public static class TaskExtenstions
{
public static SilentTask<T> IgnoreCancelAndFailure<T>(this Task<T> task)
{
return new SilentTask<T>(task);
}
}
public class SilentTask<T>
{
private readonly Task<T> _inner;
public SilentTask(Task<T> inner)
{
_inner = inner;
}
public SilentAwaiter GetAwaiter()
{
return new SilentAwaiter(_inner);
}
public class SilentAwaiter : INotifyCompletion
{
private readonly TaskAwaiter<T> _inner;
private readonly Task<T> _task;
public SilentAwaiter(Task<T> task)
{
_task = task;
_inner = task.GetAwaiter();
}
public bool IsCompleted
{
get
{
return _task.Status == TaskStatus.RanToCompletion;
}
}
public void OnCompleted(Action continuation)
{
_inner.OnCompleted(() =>
{
if (IsCompleted)
{
continuation();
}
});
}
public T GetResult()
{
return _inner.GetResult();
}
}
}
I generated a proxy with task-based operations.
How should this service be invoked properly (disposing of the ServiceClient and the OperationContext afterwards) using async/await?
My first attempt was:
public async Task<HomeInfo> GetHomeInfoAsync(DateTime timestamp)
{
using (var helper = new ServiceHelper<ServiceClient, ServiceContract>())
{
return await helper.Proxy.GetHomeInfoAsync(timestamp);
}
}
Being ServiceHelper a class which creates the ServiceClient and the OperationContextScope and disposes of them afterwards:
try
{
if (_operationContextScope != null)
{
_operationContextScope.Dispose();
}
if (_serviceClient != null)
{
if (_serviceClient.State != CommunicationState.Faulted)
{
_serviceClient.Close();
}
else
{
_serviceClient.Abort();
}
}
}
catch (CommunicationException)
{
_serviceClient.Abort();
}
catch (TimeoutException)
{
_serviceClient.Abort();
}
catch (Exception)
{
_serviceClient.Abort();
throw;
}
finally
{
_operationContextScope = null;
_serviceClient = null;
}
However, this failed miserably when calling two services at the same time with the following error: "This OperationContextScope is being disposed on a different thread than it was created."
MSDN says:
Do not use the asynchronous “await” pattern within a OperationContextScope block. When the continuation occurs, it may run on a different thread and OperationContextScope is thread specific. If you need to call “await” for an async call, use it outside of the OperationContextScope block.
So that's the problem! But, how do we fix it properly?
This guy did just what MSDN says:
private async void DoStuffWithDoc(string docId)
{
var doc = await GetDocumentAsync(docId);
if (doc.YadaYada)
{
// more code here
}
}
public Task<Document> GetDocumentAsync(string docId)
{
var docClient = CreateDocumentServiceClient();
using (new OperationContextScope(docClient.InnerChannel))
{
return docClient.GetDocumentAsync(docId);
}
}
My problem with his code, is that he never calls Close (or Abort) on the ServiceClient.
I also found a way of propagating the OperationContextScope using a custom SynchronizationContext. But, besides the fact that it's a lot of "risky" code, he states that:
It’s worth noting that it does have a few small issues regarding the disposal of operation-context scopes (since they only allow you to dispose them on the calling thread), but this doesn’t seem to be an issue since (at least according to the disassembly), they implement Dispose() but not Finalize().
So, are we out of luck here? Is there a proven pattern for calling WCF services using async/await AND disposing of BOTH the ServiceClient and the OperationContextScope? Maybe someone form Microsoft (perhaps guru Stephen Toub :)) can help.
Thanks!
[UPDATE]
With a lot of help from user Noseratio, I came up with something that works: do not use OperationContextScope. If you are using it for any of these reasons, try to find a workaround that fits your scenario. Otherwise, if you really, really, need OperationContextScope, you'll have to come up with an implementation of a SynchronizationContext that captures it, and that seems very hard (if at all possible - there must be a reason why this isn't the default behavior).
So, the full working code is:
public async Task<HomeInfo> GetHomeInfoAsync(DateTime timestamp)
{
using (var helper = new ServiceHelper<ServiceClient, ServiceContract>())
{
return await helper.Proxy.GetHomeInfoAsync(timestamp);
}
}
With ServiceHelper being:
public class ServiceHelper<TServiceClient, TService> : IDisposable
where TServiceClient : ClientBase<TService>, new()
where TService : class
{
protected bool _isInitialized;
protected TServiceClient _serviceClient;
public TServiceClient Proxy
{
get
{
if (!_isInitialized)
{
Initialize();
_isInitialized = true;
}
else if (_serviceClient == null)
{
throw new ObjectDisposedException("ServiceHelper");
}
return _serviceClient;
}
}
protected virtual void Initialize()
{
_serviceClient = new TServiceClient();
}
// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
public void Dispose()
{
Dispose(true);
// Take yourself off the Finalization queue
// to prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user's code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be disposed.
protected virtual void Dispose(bool disposing)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if (disposing)
{
try
{
if (_serviceClient != null)
{
if (_serviceClient.State != CommunicationState.Faulted)
{
_serviceClient.Close();
}
else
{
_serviceClient.Abort();
}
}
}
catch (CommunicationException)
{
_serviceClient.Abort();
}
catch (TimeoutException)
{
_serviceClient.Abort();
}
catch (Exception)
{
_serviceClient.Abort();
throw;
}
finally
{
_serviceClient = null;
}
}
}
}
Note that the class supports extension; perhaps you need to inherit and provide credentials.
The only possible "gotcha" is that in GetHomeInfoAsync, you can't just return the Task you get from the proxy (which should seem natural, why create a new Task when you already have one). Well, in this case you need to await the proxy Task and then close (or abort) the ServiceClient, otherwise you'll be closing it right away after invoking the service (while bytes are being sent over the wire)!
OK, we have a way to make it work, but it'd be nice to get an answer from an authoritative source, as Noseratio states.
I think a feasible solution might be to use a custom awaiter to flow the new operation context via OperationContext.Current. The implementation of OperationContext itself doesn't appear to require thread affinity. Here is the pattern:
async Task TestAsync()
{
using(var client = new WcfAPM.ServiceClient())
using (var scope = new FlowingOperationContextScope(client.InnerChannel))
{
await client.SomeMethodAsync(1).ContinueOnScope(scope);
await client.AnotherMethodAsync(2).ContinueOnScope(scope);
}
}
Here is the implementation of FlowingOperationContextScope and ContinueOnScope (only slightly tested):
public sealed class FlowingOperationContextScope : IDisposable
{
bool _inflight = false;
bool _disposed;
OperationContext _thisContext = null;
OperationContext _originalContext = null;
public FlowingOperationContextScope(IContextChannel channel):
this(new OperationContext(channel))
{
}
public FlowingOperationContextScope(OperationContext context)
{
_originalContext = OperationContext.Current;
OperationContext.Current = _thisContext = context;
}
public void Dispose()
{
if (!_disposed)
{
if (_inflight || OperationContext.Current != _thisContext)
throw new InvalidOperationException();
_disposed = true;
OperationContext.Current = _originalContext;
_thisContext = null;
_originalContext = null;
}
}
internal void BeforeAwait()
{
if (_inflight)
return;
_inflight = true;
// leave _thisContext as the current context
}
internal void AfterAwait()
{
if (!_inflight)
throw new InvalidOperationException();
_inflight = false;
// ignore the current context, restore _thisContext
OperationContext.Current = _thisContext;
}
}
// ContinueOnScope extension
public static class TaskExt
{
public static SimpleAwaiter<TResult> ContinueOnScope<TResult>(this Task<TResult> #this, FlowingOperationContextScope scope)
{
return new SimpleAwaiter<TResult>(#this, scope.BeforeAwait, scope.AfterAwait);
}
// awaiter
public class SimpleAwaiter<TResult> :
System.Runtime.CompilerServices.INotifyCompletion
{
readonly Task<TResult> _task;
readonly Action _beforeAwait;
readonly Action _afterAwait;
public SimpleAwaiter(Task<TResult> task, Action beforeAwait, Action afterAwait)
{
_task = task;
_beforeAwait = beforeAwait;
_afterAwait = afterAwait;
}
public SimpleAwaiter<TResult> GetAwaiter()
{
return this;
}
public bool IsCompleted
{
get
{
// don't do anything if the task completed synchronously
// (we're on the same thread)
if (_task.IsCompleted)
return true;
_beforeAwait();
return false;
}
}
public TResult GetResult()
{
return _task.Result;
}
// INotifyCompletion
public void OnCompleted(Action continuation)
{
_task.ContinueWith(task =>
{
_afterAwait();
continuation();
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
SynchronizationContext.Current != null ?
TaskScheduler.FromCurrentSynchronizationContext() :
TaskScheduler.Current);
}
}
}
Simple way is to move the await outside the using block
public Task<Document> GetDocumentAsync(string docId)
{
var docClient = CreateDocumentServiceClient();
using (new OperationContextScope(docClient.InnerChannel))
{
var task = docClient.GetDocumentAsync(docId);
}
return await task;
}
I decide to write my own code that helps with this, posting in case this helps anyone. Seems to be a little less to go wrong (unforeseen races etc) vs the SimpleAwaiter implementation above but you be the judge:
public static class WithOperationContextTaskExtensions
{
public static ContinueOnOperationContextAwaiter<TResult> WithOperationContext<TResult>(this Task<TResult> #this, bool configureAwait = true)
{
return new ContinueOnOperationContextAwaiter<TResult>(#this, configureAwait);
}
public static ContinueOnOperationContextAwaiter WithOperationContext(this Task #this, bool configureAwait = true)
{
return new ContinueOnOperationContextAwaiter(#this, configureAwait);
}
public class ContinueOnOperationContextAwaiter : INotifyCompletion
{
private readonly ConfiguredTaskAwaitable.ConfiguredTaskAwaiter _awaiter;
private OperationContext _operationContext;
public ContinueOnOperationContextAwaiter(Task task, bool continueOnCapturedContext = true)
{
if (task == null) throw new ArgumentNullException("task");
_awaiter = task.ConfigureAwait(continueOnCapturedContext).GetAwaiter();
}
public ContinueOnOperationContextAwaiter GetAwaiter() { return this; }
public bool IsCompleted { get { return _awaiter.IsCompleted; } }
public void OnCompleted(Action continuation)
{
_operationContext = OperationContext.Current;
_awaiter.OnCompleted(continuation);
}
public void GetResult()
{
OperationContext.Current = _operationContext;
_awaiter.GetResult();
}
}
public class ContinueOnOperationContextAwaiter<TResult> : INotifyCompletion
{
private readonly ConfiguredTaskAwaitable<TResult>.ConfiguredTaskAwaiter _awaiter;
private OperationContext _operationContext;
public ContinueOnOperationContextAwaiter(Task<TResult> task, bool continueOnCapturedContext = true)
{
if (task == null) throw new ArgumentNullException("task");
_awaiter = task.ConfigureAwait(continueOnCapturedContext).GetAwaiter();
}
public ContinueOnOperationContextAwaiter<TResult> GetAwaiter() { return this; }
public bool IsCompleted { get { return _awaiter.IsCompleted; } }
public void OnCompleted(Action continuation)
{
_operationContext = OperationContext.Current;
_awaiter.OnCompleted(continuation);
}
public TResult GetResult()
{
OperationContext.Current = _operationContext;
return _awaiter.GetResult();
}
}
}
Usage (a little manual and nesting is untested...):
/// <summary>
/// Make a call to the service
/// </summary>
/// <param name="action"></param>
/// <param name="endpoint"> </param>
public async Task<ResultCallWrapper<TResult>> CallAsync<TResult>(Func<T, Task<TResult>> action, EndpointAddress endpoint)
{
using (ChannelLifetime<T> channelLifetime = new ChannelLifetime<T>(ConstructChannel(endpoint)))
{
// OperationContextScope doesn't work with async/await
var oldContext = OperationContext.Current;
OperationContext.Current = new OperationContext((IContextChannel)channelLifetime.Channel);
var result = await action(channelLifetime.Channel)
.WithOperationContext(configureAwait: false);
HttpResponseMessageProperty incomingMessageProperty = (HttpResponseMessageProperty)OperationContext.Current.IncomingMessageProperties[HttpResponseMessageProperty.Name];
string[] keys = incomingMessageProperty.Headers.AllKeys;
var headersOrig = keys.ToDictionary(t => t, t => incomingMessageProperty.Headers[t]);
OperationContext.Current = oldContext;
return new ResultCallWrapper<TResult>(result, new ReadOnlyDictionary<string, string>(headersOrig));
}
}
Async flow is supported from .Net 4.6.2.
We have an ASP.Net WebApi application running on .Net 4.6 where we used the accepted answer. TaskScheduler.FromCurrentSynchronizationContext() caused deadlock issues when the current synchronization context is AspNetSynchronizationContext.
I believe the continuation task was queued after the actual task, causing the actual task is waiting on the continuation whereas the continuation task must run to complete the actual task. i.e. tasks are both waiting on each other.
So I fixed the issue by changing using continuation task to use TaskAwaiter. See: https://blogs.msdn.microsoft.com/lucian/2012/12/11/how-to-write-a-custom-awaiter/
It's been a while on this one, but I'll chime in with my own home-baked solution.
If one doesn't mind doing without OperationContextScope, one might consider something along these lines:
Extension methods
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
namespace Intexx.ServiceModel
{
public static class WcfExtensions
{
[DebuggerStepThrough]
public static void Call<TChannel>(this TChannel Client, Action<TChannel> Method) where TChannel : ICommunicationObject
{
try
{
Method.Invoke(Client);
}
finally
{
Cleanup(Client);
}
}
[DebuggerStepThrough]
public static TResult Call<TChannel, TResult>(this TChannel Client, Func<TChannel, TResult> Method) where TChannel : ICommunicationObject
{
try
{
return Method.Invoke(Client);
}
finally
{
Cleanup(Client);
}
}
[DebuggerStepThrough]
public async static Task CallAsync<TChannel>(this TChannel Client, Func<TChannel, Task> Method) where TChannel : ICommunicationObject
{
try
{
await Method.Invoke(Client);
}
finally
{
Cleanup(Client);
}
}
[DebuggerStepThrough]
public async static Task<TResult> CallAsync<TChannel, TResult>(this TChannel Client, Func<TChannel, Task<TResult>> Method) where TChannel : ICommunicationObject
{
try
{
return await Method.Invoke(Client);
}
finally
{
Cleanup(Client);
}
}
private static void Cleanup<TChannel>(TChannel Client) where TChannel : ICommunicationObject
{
try
{
if (Client.IsNotNull)
{
if (Client.State == CommunicationState.Faulted)
Client.Abort();
else
Client.Close();
}
}
catch (Exception ex)
{
Client.Abort();
if (!ex is CommunicationException && !ex is TimeoutException)
throw new Exception(ex.Message, ex);
}
finally
{
Client = null;
}
}
}
}
Client class
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
namespace Reader
{
public class Client
{
public static CemReaderClient Create()
{
Tuple<Channels.Binding, EndpointAddress, double> oService;
try
{
oService = Main.Services(typeof(ICemReader));
return new CemReaderClient(oService.Item1, oService.Item2);
}
catch (KeyNotFoundException ex)
{
return null;
}
}
}
}
Usage (in VB, as the code wouldn't convert)
Using oReader As Reader.CemReaderClient = Reader.Client.Create
If oReader.IsNotNothing Then
Dim lIsReading = Await oReader.CallAsync(Function(Reader As Reader.CemReaderClient)
Me.ConfigFilePath = If(Me.ConfigFilePath, Reader.GetConfigFilePath)
Me.BackupDrive = If(Me.BackupDrive, Reader.GetBackupDrive)
Me.SerialPort = If(Me.SerialPort, Reader.GetSerialPort)
Me.LogFolder = If(Me.LogFolder, Reader.GetLogFolder)
Return Reader.GetIsReadingAsync
End Function)
End If
End Using
I've had this running reliably in production under frequency loads of around 15 calls/sec on the client side (that's as fast as serial processing would allow). That was on a single thread, though—this hasn't been rigorously tested for thread safety. YMMV.
In my case, I decided to roll the extension methods into their own private NuGet package. The whole construct has turned out to be pretty handy.
This will have to be reevaluated, of course, if OperationContextScope ever ends up being needed.
The bit with the Tuple in the Client class is for Service Discovery support. If anyone would like to see that code as well, give a shout and I'll update my answer.
I am a little bit confused, I found this Blog : Task-based asynchronous operation in WCF
There this is a async wcf communication:
[ServiceContract]
public interface IMessage
{
[OperationContract]
Task<string> GetMessages(string msg);
}
public class MessageService : IMessage
{
async Task<string> IMessage.GetMessages(string msg)
{
var task = Task.Factory.StartNew(() =>
{
Thread.Sleep(10000);
return "Return from Server : " + msg;
});
return await task.ConfigureAwait(false);
}
}
Client:
var client = new Proxy("BasicHttpBinding_IMessage");
var task = Task.Factory.StartNew(() => client.GetMessages("Hello"));
var str = await task;
So is this also a good way??
I ran into the same issue, however it dawned on me that I didn't need to use async/await at all.
Since you are not post processing the result, there is no need to wait for the reply. If you do need to process the result, just use the old fashion TPL continuation.
public Task<MyDomainModel> GetHomeInfoAsync(DateTime timestamp)
{
using (var helper = new ServiceHelper<ServiceClient, ServiceContract>())
{
return helper.Proxy.GetHomeInfoAsync(timestamp).ContinueWith(antecedent=>processReplay(antecedent.Result));
}
}
I don't know if this helps, but after seeing this question on my search to answer the same question, I came upon this.
Leading from that, I should think your code should look something like this:
public async Task<HomeInfo> GetHomeInfoAsync(DateTime timestamp)
{
using (var client = CreateDocumentServiceClient())
{
await client.BeginGetHomeInfoAsync(timestamp);
}
}
I realise my answer comes rather late :P but it might help someone else.
My app needs to load plugins into separate app domains and then execute some code inside of them asynchronously. I've written some code to wrap Task in marshallable types:
static class RemoteTask
{
public static async Task<T> ClientComplete<T>(RemoteTask<T> remoteTask,
CancellationToken cancellationToken)
{
T result;
using (cancellationToken.Register(remoteTask.Cancel))
{
RemoteTaskCompletionSource<T> tcs = new RemoteTaskCompletionSource<T>();
remoteTask.Complete(tcs);
result = await tcs.Task;
}
await Task.Yield(); // HACK!!
return result;
}
public static RemoteTask<T> ServerStart<T>(Func<CancellationToken, Task<T>> func)
{
return new RemoteTask<T>(func);
}
}
class RemoteTask<T> : MarshalByRefObject
{
readonly CancellationTokenSource cts = new CancellationTokenSource();
readonly Task<T> task;
internal RemoteTask(Func<CancellationToken, Task<T>> starter)
{
this.task = starter(cts.Token);
}
internal void Complete(RemoteTaskCompletionSource<T> tcs)
{
task.ContinueWith(t =>
{
if (t.IsFaulted)
{
tcs.TrySetException(t.Exception);
}
else if (t.IsCanceled)
{
tcs.TrySetCancelled();
}
else
{
tcs.TrySetResult(t.Result);
}
}, TaskContinuationOptions.ExecuteSynchronously);
}
internal void Cancel()
{
cts.Cancel();
}
}
class RemoteTaskCompletionSource<T> : MarshalByRefObject
{
readonly TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
public bool TrySetResult(T result) { return tcs.TrySetResult(result); }
public bool TrySetCancelled() { return tcs.TrySetCanceled(); }
public bool TrySetException(Exception ex) { return tcs.TrySetException(ex); }
public Task<T> Task
{
get
{
return tcs.Task;
}
}
}
It's used like:
sealed class ControllerAppDomain
{
PluginAppDomain plugin;
public Task<int> SomethingAsync()
{
return RemoteTask.ClientComplete(plugin.SomethingAsync(), CancellationToken.None);
}
}
sealed class PluginAppDomain : MarshalByRefObject
{
public RemoteTask<int> SomethingAsync()
{
return RemoteTask.ServerStart(async cts =>
{
cts.ThrowIfCancellationRequested();
return 1;
});
}
}
But I've run into a snag. If you look in ClientComplete, there's a Task.Yield() I've inserted. If I comment this line, ClientComplete will never return. Any ideas?
My best guess is that you are facing these issues because of the async method that contains await and this is managed via the ThreadPool which can allocate some recycled Thread.
Reference
Best practice to call ConfigureAwait for all server-side code
Actually, just doing an await can do that(put you on a different thread). Once your async method hits
an await, the method is blocked but the thread returns to the thread
pool. When the method is ready to continue, any thread is snatched
from the thread pool and used to resume the method.
Try to streamline the code, generate threads for baseline cases and
performance is last.
I’m currently trying to make my application using some Async methods.
All my IO is done through explicit implementations of an interface and I am a bit confused about how to make the operations async.
As I see things I have two options in the implementation:
interface IIO
{
void DoOperation();
}
OPTION1:
Do an implicit implementation async and await the result in the implicit implementation.
class IOImplementation : IIO
{
async void DoOperation()
{
await Task.Factory.StartNew(() =>
{
//WRITING A FILE OR SOME SUCH THINGAMAGIG
});
}
#region IIO Members
void IIO.DoOperation()
{
DoOperation();
}
#endregion
}
OPTION2:
Do the explicit implementation async and await the task from the implicit implementation.
class IOAsyncImplementation : IIO
{
private Task DoOperationAsync()
{
return new Task(() =>
{
//DO ALL THE HEAVY LIFTING!!!
});
}
#region IIOAsync Members
async void IIO.DoOperation()
{
await DoOperationAsync();
}
#endregion
}
Are one of these implementations better than the other or is there another way to go that I am not thinking of?
Neither of these options is correct. You're trying to implement a synchronous interface asynchronously. Don't do that. The problem is that when DoOperation() returns, the operation won't be complete yet. Worse, if an exception happens during the operation (which is very common with IO operations), the user won't have a chance to deal with that exception.
What you need to do is to modify the interface, so that it is asynchronous:
interface IIO
{
Task DoOperationAsync(); // note: no async here
}
class IOImplementation : IIO
{
public async Task DoOperationAsync()
{
// perform the operation here
}
}
This way, the user will see that the operation is async and they will be able to await it. This also pretty much forces the users of your code to switch to async, but that's unavoidable.
Also, I assume using StartNew() in your implementation is just an example, you shouldn't need that to implement asynchronous IO. (And new Task() is even worse, that won't even work, because you don't Start() the Task.)
Better solution is to introduce another interface for async operations. New interface must inherit from original interface.
Example:
interface IIO
{
void DoOperation();
}
interface IIOAsync : IIO
{
Task DoOperationAsync();
}
class ClsAsync : IIOAsync
{
public void DoOperation()
{
DoOperationAsync().GetAwaiter().GetResult();
}
public async Task DoOperationAsync()
{
//just an async code demo
await Task.Delay(1000);
}
}
class Program
{
static void Main(string[] args)
{
IIOAsync asAsync = new ClsAsync();
IIO asSync = asAsync;
Console.WriteLine(DateTime.Now.Second);
asAsync.DoOperation();
Console.WriteLine("After call to sync func using Async iface: {0}",
DateTime.Now.Second);
asAsync.DoOperationAsync().GetAwaiter().GetResult();
Console.WriteLine("After call to async func using Async iface: {0}",
DateTime.Now.Second);
asSync.DoOperation();
Console.WriteLine("After call to sync func using Sync iface: {0}",
DateTime.Now.Second);
Console.ReadKey(true);
}
}
P.S.
Redesign your async operations so they return Task instead of void, unless you really must return void.
I created a sample app based on Svick's answer and found that calling IOImplementation.DoOperationAsync() without the async keyword does not result in a compiler/Visual Studio warning. This was based on Visual Studio 2019 and .NET Core 3.1.
Sample code below.
public interface ISomething
{
Task DoSomethingAsync();
}
public class Something : ISomething
{
public async Task DoSomethingAsync()
{
await Task.Run(() => Thread.Sleep(2000));
Console.WriteLine("Message from DoSomethingAsync");
throw new Exception("Some exception");
}
}
class Program
{
static void Main(string[] args)
{
ISomething something = new Something();
Console.WriteLine("pre something.DoSomethingAsync() without await");
something.DoSomethingAsync(); // No compiler warning for missing "await" and exception is "swallowed"
Console.WriteLine("post something.DoSomethingAsync() without await");
Thread.Sleep(3000);
// Output:
// pre something.DoSomethingAsync() without await
// post something.DoSomethingAsync() without await
// Message from DoSomethingAsync
}
}
An abstract class can be used instead of an interface (in C# 7.3).
// Like interface
abstract class IIO
{
public virtual async Task<string> DoOperation(string Name)
{
throw new NotImplementedException(); // throwing exception
// return await Task.Run(() => { return ""; }); // or empty do
}
}
// Implementation
class IOImplementation : IIO
{
public override async Task<string> DoOperation(string Name)
{
return await await Task.Run(() =>
{
if(Name == "Spiderman")
return "ok";
return "cancel";
});
}
}