return Task.Run in an async method - c#

How would you rewrite TaskOfTResult_MethodAsync to avoid the error: Since this is an async method, the return expression must be of type int rather than Task<int>.
private static async Task<int> TaskOfTResult_MethodAsync()
{
return Task.Run(() => ComplexCalculation());
}
private static int ComplexCalculation()
{
double x = 2;
for (int i = 1; i< 10000000; i++)
{
x += Math.Sqrt(x) / i;
}
return (int)x;
}

Simple; either don't make it async:
private static Task<int> TaskOfTResult_MethodAsync()
{
return Task.Run(() => ComplexCalculation());
}
or await the result:
private static async Task<int> TaskOfTResult_MethodAsync()
{
return await Task.Run(() => ComplexCalculation());
}
(adding the await here is more expensive in terms of the generated machinery, but has more obvious/reliable exception handling, etc)
Note: you could also probably use Task.Yield:
private static async Task<int> TaskOfTResult_MethodAsync()
{
await Task.Yield();
return ComplexCalculation();
}
(note that what this does depends a lot on the sync-context, if one)

Related

ManualResetValueTaskSourceCore usage question

I'm playing around with ManualResetValueTaskSourceCore, yet it seems even in this basic usage I have some sort of bug/misunderstanding.
The (only once) awaited ValueTask does never complete.
If the call to mre.SetResult is done before awaiting the ValueTask it works as expected.
internal struct ManualResetValueTaskSource<T> : IValueTaskSource<T>
{
private ManualResetValueTaskSourceCore<T> _taskSource = new();
public short Version => _taskSource.Version;
public ManualResetValueTaskSource()
{
_taskSource.RunContinuationsAsynchronously = true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ValueTask<T> GetTask() => new(this, _taskSource.Version);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void SetResult(T result) => _taskSource.SetResult(result);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Reset() => _taskSource.Reset();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T GetResult(short token) => _taskSource.GetResult(token);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTaskSourceStatus GetStatus(short token) => _taskSource.GetStatus(token);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnCompleted(Action<object?> continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) => _taskSource.OnCompleted(continuation, state, token, flags);
}
internal static class Program
{
private static async Task Main()
{
var mre = new ManualResetValueTaskSource<int>();
Task.Run(async () =>
{
await Task.Delay(250);
mre.SetResult(123);
});
var result = await mre.GetTask();
Console.WriteLine(result);
}
}
You are passing value type in constructor here:
internal ValueTask<T> GetTask() => new(this, _taskSource.Version);
After this you capture value type local variable in closure here:
Task.Run(async () =>
{
await Task.Delay(250);
mre.SetResult(123);
});
That means you are completing copy of mre inside anonymous function. If you change ManualResetValueTaskSource to class code works.

Failing to write async recursive iteration function

I'm trying to write a recursion method to retrieve the parent of a object (and that parent etc). This in itself isn't a problem but the calls are async which result in the following error:
The body of '.....Recursive(string)' cannot be an iterator block because 'Task<IEnumerable>' is not an iterator interface type [...]csharp(CS1624)
The code:
private async Task<IEnumerable<string>> Recursive(string objectId)
{
var result = await GetParent(objectId);
if (result?.Length > 0)
{
yield return result;
await Recursive(objectId);
}
}
private async Task<string> GetParent(string objectId)
{
await Task.Run(() => { return $"{objectId}/parent"; });
}
I have also tried IAsyncEnumerable but that resulted in the folllowing error:
'IAsyncEnumerable' does not contain a definition for 'GetAwaiter' and no accessible extension method 'GetAwaiter' accepting a first argument of type 'IAsyncEnumerable' could be found (are you missing a using directive or an assembly reference?) [...]csharp(CS1061)
private async IAsyncEnumerable<string> Recursive(string objectId)
{
var result = await GetParent(objectId);
if (result?.Length > 0)
{
yield return result;
await Recursive(objectId);
}
}
private async Task<string> GetParent(string objectId)
{
await Task.Run(() => { return $"{objectId}/parent"; });
}
I'm going to write a while loop to get this to work. But I'm interested if this is possible at all.
Update 2:
Ok, I think I got it. Thanks guys.
private async IAsyncEnumerable<string> Recursive(string objectId)
{
var result = await GetParent(objectId);
if (result?.Length > 0)
{
yield return result;
await foreach (var r2 in Recursive(objectId))
{
yield return r2;
}
}
}
private async Task<string> GetParent(string objectId)
{
await Task.Run(() => { return $"{objectId}/parent"; });
}
The current code wouldn't compile even if it was synchronous, and the result was IEnumerable<string>. The results of Recursive are never returned. It's not possible to just return an IEnumerable from an iterator either.
This code would work. Whether it does anything useful is another matter :
private IEnumerable<string> Recursive(string objectId)
{
var result = GetParent(objectId);
if (!string.IsNullOrEmpty(result))
{
yield return result;
foreach(var r in Recursive(result))
{
yield return r;
}
}
}
private string GetParent(string objectId)
{
return $"{objectId}/parent";
}
Getting it to work asynchronously only needs changing to IAsyncEnumerable and using await:
private async IAsyncEnumerable<string> Recursive(string objectId)
{
var result = await GetParent(objectId);
if (!string.IsNullOrEmpty(result))
{
yield return result;
await foreach(var r in Recursive(result))
{
yield return r;
}
}
}
private Task<string> GetParent(string objectId)
{
return Task.FromResult($"{objectId}/parent");
}

Using IEnumerator for async operation on items

I am using some API that returns me an IEnumerator child Instance i want to do some function on each object returned
I usualy use
while(Enumerator.MoveNext())
{
DoSomething(Enumerator.current);
}
I don't have IEnumerable Object
and I want to DoSomething with each object asynchronously
I don't want to get IEnumerable from IEnumerator for performance issues.
What else can i do to loop async with IEnumerator
C# 8.0 has Async Enumerable feature.
static async Task Main(string[] args)
{
await foreach (var dataPoint in FetchIOTData())
{
Console.WriteLine(dataPoint);
}
Console.ReadLine();
}
static async IAsyncEnumerable<int> FetchIOTData()
{
for (int i = 1; i <= 10; i++)
{
await Task.Delay(1000);//Simulate waiting for data to come through.
yield return i;
}
}

Task.WaitAll with CancelationToken

I need to execute many methods at the same time and the result of all, concatenate them in a single list. In the following example, I wait 3 seconds for each method, but in one of them I added a sleep (10 seconds) to check the result and it is not the expected one. The method never cancels and waits for those 10 seconds. What is the problem? Thank you!
var result = await Task.Run(() => Load<MyCustomClass>(OneMethod(), OtherMethod()));
private List<T> OneMethod()
{
return new List<T>();
}
private List<T> OtherMethod()
{
Thread.Sleep(10000);
return new List<T>();
}
private async Task<List<T>> Load<T>(params List<T>[] taskList)
{
try
{
return (await Task.WhenAll(taskList.Select(x =>
Task.Run(() => x, new CancellationTokenSource(3000).Token)))).SelectMany(x => x).ToList();
}
catch (Exception currentException)
{
//BLA BLA BLA
}
return new List<T>();
}
You must pass the CancellationToken to the methods and check if cancellationToken is requested or directly raise an exception.
var t = new CancellationTokenSource(3000).Token
var result = await Task.Run(() => Load<MyCustomClass>(OneMethod(t), OtherMethod(t)));
private List<T> OneMethod(CancellationToken t)
{
token.ThrowIfCancellationRequested();
return new List<T>();
}
private List<T> OtherMethod(CancellationToken t)
{
Thread.Sleep(10000);
token.ThrowIfCancellationRequested();
// or you can check if cancellation is requested
// if (token.IsCancellationRequested) ...
return new List<T>();
}
private async Task<List<T>> Load<T>(params List<T>[] taskList)
{
try
{
return (await Task.WhenAll(taskList.Select(x =>
Task.Run(() => x)))).SelectMany(x => x).ToList();
}
catch (Exception currentException)
{
//BLA BLA BLA
}
return new List<T>();
}
See this question

How to create Task with multiple params, return typ, TaskCreationOptions param by using new keyword?

How can i create new task with multiple params, return type and createoptions by using new?
Task<int> task = Task<int>(DoWork(0,1));
private static Task<int> DoWork(int a, int b)
{
return null;
}
this is working fine.... but when i try create task with new keyword so i can set startoptions to longrunning like this:
Task<int> task = new Task<int>(DoWork(0,1), TaskCreationOptions.LongRunning);
I am always getting some errs like:
Argument 1: cannot convert from 'System.Threading.Tasks.Task' to 'System.Func'
I tried xx different variants but no luck. I understand i am probably wrongly passing param "Func function". I would like to avoid anonymous function. thx.
You can pass the method as a Lambda Expression:
Task<Task<int>> task = new Task<Task<int>>(() => DoWork(0,1), TaskCreationOptions.LongRunning);
Although, it is recommended to use Task.Factory.StartNew if possible, so you return a Hot Task instead of a Cold Task (which required you to call Task.Start).
Task<Task<int>> task = Task.Factory.StartNew(() => DoWork(0,1), TaskCreationOptions.LongRunning);
public SomeClass()
{
var func = new Func<int, int, int>((i1, i2) => i1 + i2);
Task.Factory.StartNew(() =>
Debug.WriteLine(func(1, 2)), TaskCreationOptions.LongRunning);
Task.Factory.StartNew(() =>
Debug.WriteLine(DoWork(2, 3).Result), TaskCreationOptions.LongRunning);
}
private static Task<int> DoWork(int a, int b)
{
return Task.FromResult(a + b);
}
The constructor for Task<T> requires a Func<T> argument.
Task<int> task = Task<int>(DoWork(0,1));
is attempting to call the DoWork method and pass the returnedTask<int> as the parameter into the task task. You need to pass a Func<int> instead by changing the type of DoWork to:
private static int DoWork(int a, int b) { ... }
then you can do:
Task<int> task = new Task<int>(() => DoWork(0,1), TaskCreationOptions.LongRunning);

Categories

Resources