I'm trying to understand async actions and I'm a bit confused.
Actions are just glorified Delegates. Given the Actions
Action act = null;
act += () => { Console.WriteLine("Sync"); };
act += async () => { await File.AppendAllLinesAsync("C:/Test.Txt",
new[] { "Async File Operation" });
};
How can we invoke this async seeing as one of the delegates is async and the other is not. I've seen some extension methods in other SO answers simplified for the example would look like so:
public static void InvokeAsync(this Action action, AsyncCallback ar, object userObject = null)
{
var listeners = action.GetInvocationList();
foreach (var t in listeners)
{
var handler = (Action)t;
handler.BeginInvoke(ar, userObject);
}
}
I'm concerned if this even works because it looks like it invokes your callback for each listener which doesn't make sense.
I've only been using async with the more friendly version async/await so I do not understand this syntax as much. (I'm assuming the callback would be everything after the await and the userObject is equivalent to the dreadful SyncronizationContext that causes deadlocks if when calling sync without ConfigureAwait(false), but that is just a guess)
This is syntax inconvenient so I would perfer to use async await syntax, since async/await is called using duck-typing. I've read a blog about using async with delegates which for the example
public static class DelegateExtensions
{
public static TaskAwaiter GetAwaiter(this Action action)
{
Task task = new Task(action);
task.Start();
return task.GetAwaiter();
}
}
This too concerns me for a few reason, this looks much like an anti pattern.
Isn't this just creating a task which will run my action synchronous on a seperate thread? I also don't see this run through the invocation list.
Are either of these methods proper for invoking run delegates asynchronously?
Is there a way I can invoke an async delegate with the await syntax while still fully leveraging async?
What is the proper way to invoke async delegates with multiple functions in the invocation list?
I think Eric Lippert's comment have clarified the situation more than I could ever.
Overall, if you need to act on the return type of a method, you shouldn't use multicast delegates. If you still have to, at least use a Func<Task> signature, then you can iterate on each individual delegate using GetInvocationList, as explained here.
But would it be really impossible to work your way out of a multicast delegate with async void method?
It turns out that you can be notified of beginning and end of async void methods by using a custom synchronization context and overriding the OperationStarted and OperationCompleted methods. We can also override the Post method to set the synchronization context of child operations, to capture subsequent async void calls.
Piecing it together, you could come with something like:
class Program
{
static async Task Main(string[] args)
{
Action act = null;
act += () => { Console.WriteLine("Sync"); };
act += async () =>
{
Callback();
await Task.Delay(1000);
Console.WriteLine("Async");
};
await AwaitAction(act);
Console.WriteLine("Done");
Console.ReadLine();
}
static async void Callback()
{
await Task.Delay(2000);
Console.WriteLine("Async2");
}
static Task AwaitAction(Action action)
{
var delegates = action.GetInvocationList();
var oldSynchronizationContext = SynchronizationContext.Current;
var asyncVoidSynchronizationContext = new AsyncVoidSynchronizationContext();
try
{
SynchronizationContext.SetSynchronizationContext(asyncVoidSynchronizationContext);
var tasks = new Task[delegates.Length];
for (int i = 0; i < delegates.Length; i++)
{
((Action)delegates[i]).Invoke();
tasks[i] = asyncVoidSynchronizationContext.GetTaskForLastOperation();
}
return Task.WhenAll(tasks);
}
finally
{
SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext);
}
}
}
public class AsyncVoidSynchronizationContext : SynchronizationContext
{
private TaskCompletionSource<object> _tcs;
private Task _latestTask;
private int _operationCount;
public Task GetTaskForLastOperation()
{
if (_latestTask != null)
{
var task = _latestTask;
_latestTask = null;
return task;
}
return Task.CompletedTask;
}
public override void Post(SendOrPostCallback d, object state)
{
Task.Run(() =>
{
SynchronizationContext.SetSynchronizationContext(this);
d(state);
});
}
public override void OperationStarted()
{
if (Interlocked.Increment(ref _operationCount) == 1)
{
// First operation
_tcs = new TaskCompletionSource<object>();
_latestTask = _tcs.Task;
}
base.OperationStarted();
}
public override void OperationCompleted()
{
if (Interlocked.Decrement(ref _operationCount) == 0)
{
// Last operation
_tcs.TrySetResult(null);
}
base.OperationCompleted();
}
}
The output would be:
Sync
Async
Async2
Done
Of course, this code is provided just for recreational purpose. There's plenty of limitations, such as the fact the fact that it wouldn't work as-is if you're already using a synchronization context (such as the WPF one). I'm also certain that it has a few subtle bugs and concurrency issues here and there.
Related
I'm wondering if there is a way to create either IAsyncEnumerable<T> or IAsyncEnumerator<T> via a Source object, rather like TaskCompletionSource allows one to do for tasks. In particular, TaskCompletionSource can be passed around like any other parameter.
Maybe something like this:
public class AsyncEnumerables {
public Task HandlerTask { get; set; }
public async Task<string> ParentMethod() {
var source = new AsyncEnumerableSource<int>();
IAsyncEnumerable asyncEnumerable = source.GetAsyncEnumerable();
HandlerTask = Task.Run(() => handleAsyncResultsAsTheyHappen(asyncEnumerable));
int n = await someOtherTask();
source.YieldReturn(n);
var r = await ChildMethod(source);
source.Complete(); // this call would cause the HandlerTask to complete.
return r;
}
private async Task<string> ChildMethod(AsyncEnumerableSource<int> source) {
source.YieldReturn(5);
await SomeOtherCall();
source.YieldReturn(10);
return "hello";
}
}
With the above code, the handleAsyncResultsAsTheyHappen task would see whatever values got passed into YieldReturn. So it would see the n from the above code, as well as the 5 and the 10 from ChildMethod.
Here is another implementation of the AsyncEnumerableSource class, that doesn't depend on the Rx library. This one depends instead on the Channel<T>, class, which is natively available in the .NET standard libraries. It has identical behavior to the Rx-based implementation.
The class AsyncEnumerableSource can propagate notifications to multiple subscribers. Each subscriber can enumerate these notifications at its own pace. This is possible because each subscription has its own dedicated Channel<T> as underlying storage. The lifetime of a subscription is practically tied to the lifetime of a single await foreach loop. Breaking early from a loop for any reason (including thrown exceptions), ends immediately the subscription.
In technical terms a new subscription is created the first time that the MoveNextAsync method of an IAsyncEnumerator<T> is invoked. Calling the method GetAsyncEnumerable alone doesn't create a subscription, nor calling the GetAsyncEnumerator method does. The subscription ends when the associated IAsyncEnumerator<T> is disposed.
public class AsyncEnumerableSource<T>
{
private readonly List<Channel<T>> _channels = new();
private bool _completed;
private Exception _exception;
public async IAsyncEnumerable<T> GetAsyncEnumerable(
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
Channel<T> channel;
lock (_channels)
{
if (_exception != null) throw _exception;
if (_completed) yield break;
channel = Channel.CreateUnbounded<T>(
new() { SingleWriter = true, SingleReader = true });
_channels.Add(channel);
}
try
{
await foreach (var item in channel.Reader.ReadAllAsync()
.WithCancellation(cancellationToken).ConfigureAwait(false))
{
yield return item;
cancellationToken.ThrowIfCancellationRequested();
}
}
finally { lock (_channels) _channels.Remove(channel); }
}
public void YieldReturn(T value)
{
lock (_channels)
{
if (_completed) return;
foreach (var channel in _channels) channel.Writer.TryWrite(value);
}
}
public void Complete()
{
lock (_channels)
{
if (_completed) return;
foreach (var channel in _channels) channel.Writer.TryComplete();
_completed = true;
}
}
public void Fault(Exception error)
{
lock (_channels)
{
if (_completed) return;
foreach (var channel in _channels) channel.Writer.TryComplete(error);
_completed = true;
_exception = error;
}
}
}
The reason for the cancellationToken.ThrowIfCancellationRequested(); is because of this issue: ChannelReader.ReadAllAsync(CancellationToken) not actually cancelled mid-iteration.
Caution: in case you start propagating values with YieldReturn before any consumer has subscribed to the AsyncEnumerableSource, these values are going to be lost. No subscriber is going to observe them. To prevent this scenario you should make sure that all consumers have subscribed before starting the producers. The easiest way to do it is for the consumers to be async methods, with the await foreach being the first await inside the async method:
// Correct, synchronous subscription
async Task Consume()
{
await foreach (var item in source.GetAsyncEnumerable())
{
//...
}
}
Task consumer = Consume();
Avoid the temptation to use the Task.Run method, because in this case the subscription will occur asynchronously on a ThreadPool thread, and not synchronously with the creation of the consumer:
// Wrong, delayed subscription (possibility for unobserved values)
Task consumer = Task.Run(async () =>
{
await foreach (var item in source.GetAsyncEnumerable())
{
//...
}
});
In case that you don't want to do the subscriptions synchronously, it is possible to offload them to the ThreadPool, and await them to be established before starting the producers:
// Correct, awaited subscription
Task consumer = await Task.Factory.StartNew(async () =>
{
HeavySynchronousComputation();
await foreach (var item in source.GetAsyncEnumerable())
{
//...
}
}, default, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
The Task.Factory.StartNew(async method creates a nested Task<Task>. The outer task represents the subscription, and the inner task represents the consuming loop.
You're much better off if you can structure your code to take advantage of yield return and await foreach. E.g., this code does almost the same thing:
public async Task Consume()
{
var source = ParentMethod();
HandlerTask = Task.Run(async () => { await foreach (var item in source) { Console.WriteLine(item); } });
}
public async IAsyncEnumerable<int> ParentMethod()
{
await Task.Yield();
yield return 13;
await foreach (var item in ChildMethod())
yield return item;
}
private async IAsyncEnumerable<int> ChildMethod()
{
yield return 5;
await Task.Yield();
yield return 10;
}
However, if you really need an "async enumerable source", you need to first recognize one thing. TaskCompletionSource<T> holds the results, i.e., the T (or exception). It's acting as a container. The result can be set before the task is awaited. It's the same thing with the "async enumerable source" - you'd need it to be able to hold results before any items are taken from it. The "async enumerable source" would need to hold multiple results - in this case, a collection.
So what you're actually asking for is "a collection that can be consumed as an asynchronous enumerable". There are a few possibilities here, but the one I'd recommend is a Channel:
public async Task<string> ParentMethod()
{
var source = Channel.CreateUnbounded<int>();
var sourceWriter = source.Writer;
IAsyncEnumerable<int> asyncEnumerable = source.Reader.ReadAllAsync();
HandlerTask = Task.Run(async () => { await foreach (var item in asyncEnumerable) Console.WriteLine(item); });
await Task.Yield();
await sourceWriter.WriteAsync(13);
var r = await ChildMethod(sourceWriter);
sourceWriter.Complete();
return r;
}
private async Task<string> ChildMethod(ChannelWriter<int> sourceWriter)
{
await sourceWriter.WriteAsync(5);
await Task.Yield();
await sourceWriter.WriteAsync(10);
return "hello";
}
AFAIK the .NET platform has no built-in AsyncEnumerableSource class, but it is easy to implement one by using the System.Reactive and System.Linq.Async libraries. The System.Reactive library contains the class Subject which is a combination of an IObservable and IObserver. This is a convenient class, because you can send notifications to the IObserver interface, and subscribe independently any number of times to the IObservable interface to receive these notifications back. Actually it's not required to subscribe manually, because the System.Linq.Async library contains the handy extension method ToAsyncEnumerable, that converts an IObservable to IAsyncEnumerable automatically.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Subjects;
public class AsyncEnumerableSource<T>
{
private readonly Subject<T> _subject = new Subject<T>();
public IAsyncEnumerable<T> GetAsyncEnumerable() => _subject.ToAsyncEnumerable();
public void YieldReturn(T value) => _subject.OnNext(value);
public void Complete() => _subject.OnCompleted();
public void Fault(Exception ex) => _subject.OnError(ex);
}
This implementation will send to the subscribers only the notifications that occurred after their subscription. If you want to ensure that late joiners will get the early messages, you could replace the Subject with a ReplaySubject. This one buffers the notifications it receives, so it comes with memory usage considerations: it accepts an int bufferSize argument in its constructor.
Note: The above implementation is thread-safe, although the Subject<T> class is not synchronized, and in general calling OnNext from multiple threads in parallel breaks the Rx contract. That's because the ToAsyncEnumerable operator does not depend on the Rx contract for its correctness, and synchronizes the incoming notifications. It's not a particularly efficient implementation though. Channel<T>-based implementations are significantly more efficient under heavy load.
can you please help me understand the difference between the two methods here:
1) why does the first use the thread pool and the second doesn't?
2) Why does the second give you scalability, but the first doesn't - is this related to thread pools?
3) How can I call the two methods in main to make their purpose more obvious (and better high light their differences)?
4) I know async Task methods can be called with await, what about Tasks that lack async like these methods?
public class _12_MyClass
{
public Task SleepAsyncA(int millisecondsTimeout)
{
return Task.Run(() => Thread.Sleep(millisecondsTimeout));
}
public Task SleepAsyncB(int millisecondsTimeout)
{
TaskCompletionSource<bool> tcs = null;
var t = new Timer(delegate { tcs.TrySetResult(true); }, null, -1, -1);
tcs = new TaskCompletionSource<bool>(t);
t.Change(millisecondsTimeout, -1);
return tcs.Task;
}
public static void Main()
{
}
}
I am trying to generate different objects and insert each object into it's respective List using await and async using the code below:
static List<ClassA> classAList = new List<ClassA>();
static List<ClassB> classBList = new List<ClassB>();
public async void GenerateUsers()
{
await GenerateClassA();
await GenerateClassB();
}
private static Task GenerateClassA()
{
while(true)
{
Thread.Sleep(3000);
classAList .Add(new ClassA());
Console.WriteLine(classAList.Count);
}
}
private static Task GenerateClassB()
{
while (true)
{
Thread.Sleep(6000);
classBList .Add(new ClassB());
Console.WriteLine(classBList.Count)
}
}
When I call GenerateUsers like
UserContainer uc = new UserContainer(); //Class GenerateUsers is defined
uc.GenerateUsers();
Console.WriteLine("Generating.....");
The program does not exit GenerateClassA and Generating..... is not printed to the screen.
How can I generate different objects in an infinite loop using await and async for each infinite loop.
You are using await GenerateClassA();, but GenerateClassA doesn't do anything continuation related - it simply run to completion promising to return a Task. Except: it can't run to completion, because while(true). So yes, this won't work.
Something doesn't become async just beacause you add async. That enables genuinely async operations to be coordinated: nothing more, nothing less.
The methods you are calling are not async, and so they execute synchronously. Indeed, only the first method, GenerateClassA(), is ever even called. That method never returns, so you never get as far as calling the second. Probably you wanted something more like this:
public void GenerateUsers()
{
GenerateClassA();
GenerateClassB();
}
private static async void GenerateClassA()
{
while(true)
{
await Task.Delay(3000);
classAList.Add(new ClassA());
Console.WriteLine(classAList.Count);
}
}
private static async void GenerateClassB()
{
while (true)
{
await Task.Delay(6000);
classBList.Add(new ClassB());
Console.WriteLine(classBList.Count)
}
}
Note that the void return types prevent you from observing the tasks, such as exceptions that might occur. It's not advised. But it's consistent with the code you originally posted.
I'm working on a series of methods that execute many different database calls using entity framework. Many of these methods can run asynchronously as I really don't care about their output, and don't rely on them.
However, when I try implementing certain methods, I get a warning from the compiler saying: "Because this call is not awaited, the current method continues to run before the call is completed"
But to me, this seems like that's my desired behavior, as I don't care what the methods do.
Here's an example of the methods
public async Task SetupAccessControl(int objectTypeId, int objectId, int? organizationId)
{
using (var context = new SupportContext(CustomerId))
{
... // omitted for brevity
if (objectTypeId == (int) ObjectType.User)
{
AddToUserRoleBridge("Everyone", objectId);//Warning on this line
AddToUserRoleBridge("Default", objectId); //Warning on this line
}
... // omitted for brevity
}
}
public async Task AddToUserRoleBridge(string role, int userId)
{
using (var context = new SupportContext(CustomerId))
{
var defaultRole = context.Roles.FirstOrDefault(n => n.Name == role);
if (defaultRole != null)
{
var urb = new UserRoleBridge
{
RoleId = defaultRole.Id,
UserId = userId
};
context.UserRoleBridges.Add(urb);
await context.SaveChangesAsync();
}
}
}
edit
Essentially, when I run the main function, I want a series of method calls to all fire off somewhat simultaneously and handle everything in their own threads so that I don't have to worry about it. Here is a pseudo-code example.
public async void RunAllAsync() {
taskA(*some value*);
taskA(*some value*);
taskB(*some value*);
taskB(*some value*);
await AllTasksCompleted
}
public async Task taskA(int item){
//do something with item
}
public async Task taskB(int item) {
subTaskB(*some value*)
subTaskB(*some value*)
}
public async Task subTaskB(int item) {
// do something
}
In the above example, when #RunAllAsync is called, every function call it makes (and the function calls they make) are fired off simultaneously. When all of these calls are completed, whatever method called #RunAllAsync would continue to execute.
If you're not using await, the async keyword doesn't really do anything useful and you can leave it off. You can await a method returning a Task regardless of whether it's marked as async or not.
public Task DoSomethingAsync()
{
Task someTaskJustLikeANormalReturnValue = Task.Delay(1000);
return someTaskJustLikeANormalReturnValue;
}
// later...
public async Task SomeOtherFunction()
{
// You can await DoSomethingAsync just like any async method, because
// you're really awaiting the Task which got returned.
await DoSomethingAsync();
}
In your case I would probably collect the tasks and await them all together:
public async Task SetupAccessControl(int objectTypeId, int objectId, int? organizationId)
{
var tasks = new List<Task>();
using (var context = new SupportContext(CustomerId))
{
... // omitted for brevity
if (objectTypeId == (int) ObjectType.User)
{
tasks.Add(AddToUserRoleBridge("Everyone", objectId));
tasks.Add(AddToUserRoleBridge("Default", objectId));
}
... // omitted for brevity
}
await Task.WhenAll(tasks.ToArray());
}
This allows you to pass the decision up to the caller of whether to await on the subtasks or not. This will also allow the any caller to unwrap any exceptions if they happen in AddToUserRoleBridge.
You can do it two ways:
Change the signature of AddToUserRoleBridge to return void will
remove the warning (but nothing can await it then).
Store the result of the call. var ignoreme=AddToUserRoleBridge(...) will remove the warning as well.
I am trying to block RequestHandler.ParseAll() with await ConsumerTask;, but when i set a breakpoint there, i ALWAYS get the "Done..." output first... and then Parse2() fails with a NullReferenceException. (thats my guess: "the GC starts cleaning up because _handler got out of scope")
Anyway, I can't figure out why that happens.
class MainClass
{
public async void DoWork()
{
RequestHandler _handler = new RequestHandler();
string[] mUrls;
/* fill mUrls here with values */
await Task.Run(() => _handler.ParseSpecific(mUrls));
Console.WriteLine("Done...");
}
}
static class Parser
{
public static async Task<IEnumerable<string>> QueryWebPage(string url) { /*Query the url*/ }
public static async Task Parse1(Query query)
{
Parallel.ForEach(/*Process data here*/);
}
public static async Task Parse2(Query query)
{
foreach(string line in query.WebPage)
/* Here i get a NullReference exception because query.WebPage == null */
}
}
sealed class RequestHandler
{
private BlockingCollection<Query> Queue;
private Task ConsumerTask = Task.Run(() => /* call consume() for each elem in the queue*/);
private async void Consume(Query obj)
{
await (obj.BoolField ? Parser.Parse1(obj) : Parser.Parse2(obj));
}
public async void ParseSpecific(string[] urls)
{
foreach(string v in urls)
Queue.Add(new Query(await QueryWebPage(v), BoolField: false));
Queue.CompleteAdding();
await ConsumerTask;
await ParseAll(true);
}
private async Task ParseAll(bool onlySome)
{
ReInit();
Parallel.ForEach(mCollection, v => Queue.Add(new Query(url, BoolField:false)));
Queue.CompleteAdding();
await ConsumerTask;
/* Process stuff further */
}
}
struct Query
{
public readonly string[] WebPage;
public readonly bool BoolField;
public Query(uint e, IEnumerable<string> page, bool b) : this()
{
Webpage = page.ToArray();
BoolField = b;
}
}
CodesInChaos has spotted the problem in comments. It stems from having async methods returning void, which you should almost never do - it means you've got no way to track them.
Instead, if your async methods don't have any actual value to return, you should just make them return Task.
What's happening is that ParseSpecific is only running synchronously until the first await QueryWebPage(v) that doesn't complete immediately. It's then returning... so the task started here:
await Task.Run(() => _handler.ParseSpecific(mUrls));
... completes immediately, and "Done" gets printed.
Once you've made all your async methods return Task, you can await them. You also won't need Task.Run at all. So you'd have:
public async void DoWork()
{
RequestHandler _handler = new RequestHandler();
string[] mUrls;
await _handler.ParseSpecific(mUrls);
Console.WriteLine("Done...");
}
...
public async TaskParseSpecific(string[] urls)
{
foreach(string v in urls)
{
// Refactored for readability, although I'm not sure it really
// makes sense now that it's clearer! Are you sure this is what
// you want?
var page = await QueryWebPage(v);
Queue.Add(new Query(page, false);
}
Queue.CompleteAdding();
await ConsumerTask;
await ParseAll(true);
}
Your Reinit method also needs changing, as currently the ConsumerTask will basically complete almost immediately, as Consume will return immediately as it's another async method returning void.
To be honest, what you've got looks very complex, without a proper understanding of async/await. I would read up more on async/await and then probably start from scratch. I strongly suspect you can make this much, much simpler. You might also want to read up on TPL Dataflow which is designed to make producer/consumer scenarios simpler.