Task, Action in same dictionary or alternative architecture? - c#

I hope I can get this explained right on the first try:
I have a Dictionary<int, Action> MyActions. I have a method that looks like this:
int actionNumber = 0;
while (isRunning) {
if (Phases.TryGetValue(actionNumber, out var action))
{
action.Invoke();
}
actionNumber++;
}
The idea is that the methods are run after each other whenever the one before has finished. And when there is an error I have the actionNumber saved so I can return to the correct last Method and continue from there.
The Methods I put into the Dictionary were initially only simple void Methods. Now I found myself I also have to put async Methods in there cause some of them have await in their body. This means the dictionary would have to keep void Methods and async Task Methods inside them. And the while loop must have an await or else it will just continue when it runs a async Task Method.
My Solutions:
Solution A:
Make the dictionary a Dictionary<int, Task> MyActions and make everything a Task even if it is a simple void Method. Change the while loop to:
int actionNumber = 0;
while (isRunning) {
if (Phases.TryGetValue(actionNumber, out var task))
{
Task.Run(() => task).Wait();
}
actionNumber++;
}
Solution B:
Keep everything as it is. Wrap the async Task Methods into another void Method which itself awaits the Task Method. Put this void Method inside the action dictionary.
Solution C:
Turn every async Task Method into void Method and rewrite await to .Result.
Any other better solutions? I tend to go with Solution A because it is easier for other developers to look at the Dictionary and clearly see it all has to be Tasks.

How about Dictionary<int, Func<Task>>?
You can add all async methods there and await them while they are executed. If you want to add a non-async method returning void simply wrap it with:
Func<Task> wrapper = () => {
simpleSyncMethod();
return Task.CompletedTask;
}

Related

C# wait for a function that returns void to finish a System.NET.WebClient.DownloadFileAsync() while still keeping an active WinForms UI [duplicate]

How can I wait for a void async method to finish its job?
for example, I have a function like below:
async void LoadBlahBlah()
{
await blah();
...
}
now I want to make sure that everything has been loaded before continuing somewhere else.
Best practice is to mark function async void only if it is fire and forget method, if you want to await on, you should mark it as async Task.
In case if you still want to await, then wrap it like so await Task.Run(() => blah())
If you can change the signature of your function to async Task then you can use the code presented here
The best solution is to use async Task. You should avoid async void for several reasons, one of which is composability.
If the method cannot be made to return Task (e.g., it's an event handler), then you can use SemaphoreSlim to have the method signal when it is about to exit. Consider doing this in a finally block.
You don't really need to do anything manually, await keyword pauses the function execution until blah() returns.
private async void SomeFunction()
{
var x = await LoadBlahBlah(); <- Function is not paused
//rest of the code get's executed even if LoadBlahBlah() is still executing
}
private async Task<T> LoadBlahBlah()
{
await DoStuff(); <- function is paused
await DoMoreStuff();
}
T is type of object blah() returns
You can't really await a void function so LoadBlahBlah() cannot be void
I know this is an old question, but this is still a problem I keep walking into, and yet there is still no clear solution to do this correctly when using async/await in an async void signature method.
However, I noticed that .Wait() is working properly inside the void method.
and since async void and void have the same signature, you might need to do the following.
void LoadBlahBlah()
{
blah().Wait(); //this blocks
}
Confusingly enough async/await does not block on the next code.
async void LoadBlahBlah()
{
await blah(); //this does not block
}
When you decompile your code, my guess is that async void creates an internal Task (just like async Task), but since the signature does not support to return that internal Tasks
this means that internally the async void method will still be able to "await" internally async methods. but externally unable to know when the internal Task is complete.
So my conclusion is that async void is working as intended, and if you need feedback from the internal Task, then you need to use the async Task signature instead.
hopefully my rambling makes sense to anybody also looking for answers.
Edit:
I made some example code and decompiled it to see what is actually going on.
static async void Test()
{
await Task.Delay(5000);
}
static async Task TestAsync()
{
await Task.Delay(5000);
}
Turns into (edit: I know that the body code is not here but in the statemachines, but the statemachines was basically identical, so I didn't bother adding them)
private static void Test()
{
<Test>d__1 stateMachine = new <Test>d__1();
stateMachine.<>t__builder = AsyncVoidMethodBuilder.Create();
stateMachine.<>1__state = -1;
AsyncVoidMethodBuilder <>t__builder = stateMachine.<>t__builder;
<>t__builder.Start(ref stateMachine);
}
private static Task TestAsync()
{
<TestAsync>d__2 stateMachine = new <TestAsync>d__2();
stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
stateMachine.<>1__state = -1;
AsyncTaskMethodBuilder <>t__builder = stateMachine.<>t__builder;
<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
neither AsyncVoidMethodBuilder or AsyncTaskMethodBuilder actually have any code in the Start method that would hint of them to block, and would always run asynchronously after they are started.
meaning without the returning Task, there would be no way to check if it is complete.
as expected, it only starts the Task running async, and then it continues in the code.
and the async Task, first it starts the Task, and then it returns it.
so I guess my answer would be to never use async void, if you need to know when the task is done, that is what async Task is for.
do a AutoResetEvent, call the function then wait on AutoResetEvent and then set it inside async void when you know it is done.
You can also wait on a Task that returns from your void async
You can simply change the return type to Task and call await Task.CompletedTask at the end of the function, e.g:
async Task MyFunction() {
await AnotherFunction();
await Task.CompletedTask;
}
I find this simpler than wrapping the whole function body in a call to Task.Run(() => { ... });.
I've read all the solutions of the thread and it's really complicated... The easiest solution is to return something like a bool:
async bool LoadBlahBlah()
{
await blah();
return true;
}
It's not mandatory to store or chekc the return value. You can juste do:
await LoadBlahBlah();
... and you can return false if something goes wrong.

Why won't this async/await run asynchronously?

I've searched for the answer to this but according to many guides and SO questions this code still appears correct to me, yet it runs synchronously.
private void CheckConditions()
{
foreach (var obj in myObjects)
{
if (obj.ConditionMet)
{
HandleConditionAsync(obj);
}
}
DoOtherWork();
}
private async void HandleConditionAsync(MyObject obj)
{
// shouldn't control transfer back to CheckConditions() here while we wait for user input?
string userInput = await obj.MessagePromptAsync("hello user");
DoSomeBookkeeping(obj);
}
// (MyObject.cs)
private MessagePrompt messagePrompt; // inherits from UserControl
public async Task<string> MessagePromptAsync(string prompt)
{
return await Task.FromResult<string>(messagePrompt.Prompt(prompt));
}
// (MessagePrompt.cs)
public string Prompt(string prompt)
{
this.UIThread(() => this.SetMessagePrompt(prompt));
userInputAutoResetEvent.WaitOne();
return myResult; // set in a button handler that also sets the AutoResetEvent
}
I'm intending for CheckConditions() to continue along merrily but instead it is stuck on MessagePrompt's AutoResetEvent despite my async/awaits. The only thing I can figure might be wrong is that perhaps MessagePrompt's methods aren't able to run asynchronously due to some restriction from UserControl, its use of a UI thread reference, or maybe non-async methods at the top of the stack.
There's nothing in your code that's asynchronous. The only task you have, you created from a result value, meaning the Prompt() method has to complete and return its result before you'll even get the Task object back to wait on. That object will already be completed, so any await on it will complete immediately, once it has the Task to wait on.
Maybe you meant this instead:
public async Task<string> MessagePromptAsync(string prompt)
{
return await Task.Run(() => messagePrompt.Prompt(prompt));
}
Or alternatively (if you really do have nothing else in the MessagePromptAsync() method):
public Task<string> MessagePromptAsync(string prompt)
{
return Task.Run(() => messagePrompt.Prompt(prompt));
}
Note that this may lead to a different problem, depending on what DoOtherWork() and UIThread() actually do. If your UI thread gets tied up in DoOtherWork() and the UIThread() method is wrapping Dispatcher.Invoke() or similar, then you'll have a deadlock.
If that does not address your problem, please provide a good Minimal, Complete, and Verifiable code example that reliably reproduces the problem.
You need to make CheckConditions() async as well, and then await the call to HandleConditionAsync(MyObject obj). CheckConditions() runs synchronously in your sample.
private async Task CheckConditionsAsync()
{
foreach (var obj in myObjects)
{
if (obj.ConditionMet)
{
await HandleConditionAsync(obj);
}
}
DoOtherWork();
}
Also, and this is just a best practices thing, an async method should always return a Task when possible. The only time I've ever had to use async void is for compatibility with an event handler. You can see I've changed CheckConditions() this way, and HandleConditionAsync(MyObject obj) should be modified similarly. I also changed the method name to represent it's asynchronous behaviour.
If you need to run a method that returns a Task synchronously (and you shouldn't do this, this is an indication of something incorrect about your design), you can run it with Task.FromResult(MyMethodAsync()). Again, avoid doing this wherever you can, it defeats the purpose of making a method asynchronous to in the first place.

How to await a Task inside of a test method

In my repository's unit test project, I'm creating a cleanup method to delete all of the records that were created by tests. Here is what I'm trying to do:
private void DeleteTestExtensions()
{
var authorization = _extensionRepository.GetAuthorizationByTaxAccountIdAndYear(_testTaxAccountId, _testTaxAccountYear);
var extensions = await _extensionRepository.GetExtensionsByUserIdAsync(_testUserId);
foreach (var extension in extensions)
{
try
{
_extensionRepository.DeleteExtension(extension.ExtensionID);
}
}
_extensionRepository.DeleteAuthorization(authorization.ExtensionAuthorizationID);
}
The first one is synchronous and works as expected, but the second method is asynchronous and tells me the await operator can only be used on an async method, but whenever I hover over GetExtensionsByUserIdAsync, I can see (plus I know) that it's defined as
(awaitable) Task<List<Extension>> IExtensionRepository.GetExtensionsByUserIdAsync(Guid userID)
So why would it tell me that I can't use await when it's clearly async?
The message is actually relating to DeleteTestExtensions which must be marked as async, not GetExtensionsByUserIdAsync.
So your method signature would become:
private async Task DeleteTestExtensions()
Note marking the method as returning Task means it can in turn be awaited.

async and await not returning to caller as expected

I have a simple async and await example I'm trying to work through and the execution is not returning to the caller as I expect. Here is the top level method:
protected async void MyDDL_SelectedIndexChanged(object sender, EventArgs e)
{
Task longRunningTask = LongRunningOperationAsync();
DoOtherStuff1();
DoOtherStuff2();
DoOtherStuff3();
await longRunningTask;
}
Here is the LongRunningOperationAsync method which does not work as expected and runs synchronously:
private async Task LongRunningOperationAsync()
{
var myValues = await GetStuffViaLongRunningTask();
//Code to work with myValues here...
}
Here is the definition of GetStuffViaLongRunningTask
private async Task<IList<MyClass>> GetStuffViaLongRunningTask()
{
//...Calls to get and build up IList<MyClass>
return results;
}
The problem is the above code does not return to the caller and begin running the DoOtherStuff1(); method as I would expect. However, instead of calling my own method and replacing it with a call to await Task.Delay(10000); like all the simple examples show, the code works as expected:
private async Task LongRunningOperationAsync()
{
//Returns to caller as expected:
await Task.Delay(10000);
}
The caller using the code above has longRunningTask with a WaitingForActivation as its status instead of RanToCompletion, showing it is still processing.
You might say my GetStuffViaLongRunningTask() method runs so quickly and I just can't see the results. However it always takes between 3-7 seconds to run and you can tell when debugging that the call is blocking and synchronous.
What am I doing incorrectly here, so that my call to LongRunningOperationAsync() is not working asynchronously when reaching the await word to call LongRunningOperationAsync within that method?
Assuming that //...Calls to get and build up IList<MyClass> is synchronous CPU bound work, the issue there is that GetStuffViaLongRunningTask won't return until it either ends, or hits its first await call. You should be getting a compiler warning on that method as it's an async method that doesn't have an await in it.
Instead, the method simply shouldn't be async, to clearly indicate to it's callers that it's synchronous work. Just adjust the signature to:
private IList<MyClass> GetStuffViaLongRunningTask()
Then when calling it use Task.Run to ensure that the long running CPU bound work is done in another thread:
private async Task LongRunningOperationAsync()
{
var myValues = await Task.Run(() => GetStuffViaLongRunningTask());
//Code to work with myValues here...
}
//...Calls to get and build up IList<MyClass>
You need to show us which calls are being made. If you want to use async/await with this structure then you need to make an async call.
If your GetStuffViaLongRunningTask function is not doing async calls then you can start a new task like the following:
private Task<IList<MyClass>> GetStuffViaLongRunningTask()
{
return Task.Factory.StartNew(() =>
{
//...Calls to get and build up IList<MyClass>
// You can make synchronous calls here
return list;
});
}

async Methods correct? Resharper warning

in my Method RecalcChartAsync i do some time intensive stuff.. so i thought i'll do some things async.
I want to start the two Methods CreateHistogramAsync CalculatePropValuesAsync and in the meanwhile do some stuff in my RecalcChartsAsync and finally wait for it to complete.
private async void RecalcChartsAsync()
{
var histogram = CreateHistogramAsync();
var propValues = CalculatePropValuesAsync();
//do some other stuff
await histogram;
await propValues;
}
private async Task CreateHistogramAsync()
{
//do some stuff
}
private async Task CalculatePropValuesAsync()
{
//do some stuff
}
Im not sure if i am doing it the right way because ReSharper gives me the following warning at the async Keyword at CreateHistogramAsync and CalculatePropValueAsync:
This async method lacks 'await' operators and will run synchronously. Consider using the await operator to await non-blocking API calls, ...
Now i am not sure if i am using this async thing in the correct way.
Now i am not sure if i am using this async thing in the correct way.
It doesn't sound like it. Just because you have an async method doesn't mean it's going to run on a separate thread - and it sounds like that's what you're expecting. When you execute an async method, it will run synchronously - i.e. just like normal - until it hits the first await expression. If you don't have any await expressions, that means it will just run as normal, the only difference being the way that it's wrapped up in a state machine, with the completion status (exceptions etc) represented by a task.
I suspect you should change your CreateHistogramAsync and CalculatePropValuesAsync methods to be just synchronous:
private void CreateHistogram()
{
...
}
private void CalculatePropValues()
{
...
}
and use Task.Run to execute them in parallel:
private async void RecalcChartsAsync()
{
var histogram = Task.Run((Action) CreateHistogram);
var propValues = Task.Run((Action) CalculatePropValues);
//do some other stuff
await histogram;
await propValues;
}
There is nothing you're awaiting on within your methods, so marking then as async is pointless. That's why ReSharper is warning you.
You should start from learning how async/await works: http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx
If you are not awaiting anything in your two last methods, then remove the async from the declaration. In this case, creating and returning the task will be enough to achieve what you want.

Categories

Resources