Task.WaitAll with CancelationToken - c#

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

Related

Make IObservable behave like it is pull-based

I'm working on some old code that does not have IAsyncEnumerable, so I was trying to mimic IAsyncEnumerable behavior with IObservable. The issue is that IObservable is push-based and is giving results in random order.
Here is a very simplified version of what I want to achieve
public static class Program
{
public static void Main()
{
Test().Wait();
Console.ReadLine();
}
private static async Task Test()
{
var l = await GetFirstObservable()
.SelectMany(i =>
{
return GetSecondObservable()
.Select(s => Tuple.Create(i, s));
})
.Take(3)
.ToList();
foreach (var item in l)
{
Console.WriteLine(item);
}
}
private static async Task<T> Echo<T>(T value)
{
Console.WriteLine(value);
await Task.Delay(1);
return value;
}
private static IObservable<int> GetFirstObservable()
{
return Observable.Create(async (IObserver<int> observer, CancellationToken cancellationToken) =>
{
foreach (var i in new [] {1, 2, 3})
{
if (cancellationToken.IsCancellationRequested)
{
return;
}
var e = await Echo(i);
if (cancellationToken.IsCancellationRequested)
{
return;
}
observer.OnNext(e);
}
});
}
private static IObservable<string> GetSecondObservable()
{
return Observable.Create(async (IObserver<string> observer, CancellationToken cancellationToken) =>
{
foreach (var i in new[] { "A", "B" })
{
if (cancellationToken.IsCancellationRequested)
{
return;
}
var e = await Echo(i);
if (cancellationToken.IsCancellationRequested)
{
return;
}
observer.OnNext(i);
}
});
}
}
I want the following results to be in console:
1
A
B
2
A
(1, A)
(1, B)
(2, A)
Instead, the first observable does not wait for the second observable to complete, and I get random results like:
1
A
2
A
3
B
A
B
(1, A)
(2, A)
(1, B)
Is it even possible to achieve this pull-like behavior, or should I just look for something else?

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");
}

How to GroupBy by result of async Task<string> method and get a grouping by string in C#?

I have the below method (it's an extension method but not relevant to this question) and I would like to use GroupBy on the results of the method.
class MyClass
{
public async Task<string> GetRank()
{
return "X";
}
public async static Task Test()
{
List<MyClass> items = new List<MyClass>() { new MyClass() };
var grouped = items.GroupBy(async _ => (await _.GetRank()));
}
}
The type of grouped is IGrouping<Task<string>, MyClass>, however I need to group by the actual awaited result of the async method (string). Despite using await and making the lambda async, I still get IGrouping<Task<string>, ..> instead of IGrouping<string, ...>
How to use GroupBy and group by a result of async Task<string> method and get a grouping by string?
You probably are looking to await all your tasks first, then group
// projection to task
var tasks = items.Select(y => AsyncMethod(y);
// Await them all
var results = await Task.WhenAll(tasks)
// group stuff
var groups = results.GroupBy(x => ...);
Full Demo here
Note : You didnt really have any testable code so i just plumbed up something similar
Update
the reason why you example isn't working
items.GroupBy(async _ => (await _.GetRank()))
is because and async lambda is really just a method that returns a task, this is why you are getting IGrouping<Task<string>, MyClass>
You need to wait for all you tasks to finish first before you can think about doing anything with the results from the task
To further explain what is happening take a look at this SharpLab example
Your async lambda basically resolves to this
new Func<int, Task<string>>(<>c__DisplayClass1_.<M>b__0)
Here is an asynchronous version of GroupBy. It expects a task as the result of keySelector, and returns a task that can be awaited:
public static async Task<IEnumerable<IGrouping<TKey, TSource>>>
GroupByAsync<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, Task<TKey>> keySelector)
{
var tasks = source.Select(async item => (Key: await keySelector(item), Item: item));
var entries = await Task.WhenAll(tasks);
return entries.GroupBy(entry => entry.Key, entry => entry.Item);
}
It can be used like this:
class MyClass
{
public async Task<string> GetRank()
{
await Task.Delay(100);
return "X";
}
public async static Task Test()
{
var items = new List<MyClass>() { new MyClass(), new MyClass() };
var grouped = items.GroupByAsync(async _ => (await _.GetRank()));
foreach (var grouping in await grouped)
{
Console.WriteLine($"Key: {grouping.Key}, Count: {grouping.Count()}");
}
}
}
Output:
Key: X, Count: 2

How to add an async "await" to an addrange select statement?

I have a function like this:
public async Task<SomeViewModel> SampleFunction()
{
var data = service.GetData();
var myList = new List<SomeViewModel>();
myList.AddRange(data.select(x => new SomeViewModel
{
Id = x.Id,
DateCreated = x.DateCreated,
Data = await service.GetSomeDataById(x.Id)
}
return myList;
}
My await isn't working as it can only be used in a method or lambda marked with the async modifier. Where do I place the async with this function?
You can only use await inside an async method/delegate. In this case you must mark that lambda expression as async.
But wait, there's more...
Select is from the pre-async era and so it doesn't handle async lambdas (in your case it would return IEnumerable<Task<SomeViewModel>> instead of IEnumerable<SomeViewModel> which is what you actually need).
You can however add that functionality yourself (preferably as an extension method), but you need to consider whether you wish to await each item before moving on to the next (sequentialy) or await all items together at the end (concurrently).
Sequential async
static async Task<TResult[]> SelectAsync<TItem, TResult>(this IEnumerable<TItem> enumerable, Func<TItem, Task<TResult>> selector)
{
var results = new List<TResult>();
foreach (var item in enumerable)
{
results.Add(await selector(item));
}
return results.ToArray();
}
Concurrent async
static Task<TResult[]> SelectAsync<TItem, TResult>(this IEnumerable<TItem> enumerable, Func<TItem, Task<TResult>> selector)
{
return Task.WhenAll(enumerable.Select(selector));
}
Usage
public Task<SomeViewModel[]> SampleFunction()
{
return service.GetData().SelectAsync(async x => new SomeViewModel
{
Id = x.Id,
DateCreated = x.DateCreated,
Data = await service.GetSomeDataById(x.Id)
}
}
You're using await inside of a lambda, and that lambda is going to be transformed into its own separate named method by the compiler. To use await it must itself be async, and not just be defined in an async method. When you make the lambda async you now have a sequence of tasks that you want to translate into a sequence of their results, asynchronously. Task.WhenAll does exactly this, so we can pass our new query to WhenAll to get a task representing our results, which is exactly what this method wants to return:
public Task<SomeViewModel[]> SampleFunction()
{
return Task.WhenAll(service.GetData().Select(
async x => new SomeViewModel
{
Id = x.Id,
DateCreated = x.DateCreated,
Data = await service.GetSomeDataById(x.Id)
}));
}
Though maybe too heavyweight for your use case, using TPL Dataflow will give you finer control over your async processing.
public async Task<List<SomeViewModel>> SampleFunction()
{
var data = service.GetData();
var transformBlock = new TransformBlock<X, SomeViewModel>(
async x => new SomeViewModel
{
Id = x.Id,
DateCreated = x.DateCreated,
Data = await service.GetSomeDataById(x.Id)
},
new ExecutionDataflowBlockOptions
{
// Let 8 "service.GetSomeDataById" calls run at once.
MaxDegreeOfParallelism = 8
});
var result = new List<SomeViewModel>();
var actionBlock = new ActionBlock<SomeViewModel>(
vm => result.Add(vm));
transformBlock.LinkTo(actionBlock,
new DataflowLinkOptions { PropagateCompletion = true });
foreach (var x in data)
{
transformBlock.Post(x);
}
transformBlock.Complete();
await actionBlock.Completion;
return result;
}
This could be substantially less long-winded if service.GetData() returned an IObservable<X> and this method returned an IObservable<SomeViewModel>.

Issue creating generic wrapper for async library

I have a class Library https://github.com/trydis/FIFA-Ultimate-Team-2014-Toolkit I want to make a wrapper for, instead of just calling FutClient I want to make a FutClientWrapper where I can handle exceptions, reloggin and loggin the interface I want to wrap is https://github.com/trydis/FIFA-Ultimate-Team-2014-Toolkit/blob/master/UltimateTeam.Toolkit/IFutClient.cs
So I made http://pastebin.com/0EJdCbbr
Code snippet
public async Task<AuctionResponse> SearchAsync(SearchParameters searchParameters)
{
return await Invoke(f => f.SearchAsync(searchParameters), searchParameters);
}
public async Task<AuctionResponse> PlaceBidAsync(AuctionInfo auctionInfo, uint bidAmount = 0)
{
return await Invoke(f => f.PlaceBidAsync(auctionInfo, bidAmount), auctionInfo);
}
public async Task<Item> GetItemAsync(long resourceId)
{
return await Invoke(f => f.GetItemAsync(resourceId), resourceId);
}
public TResult Invoke<TResult>(Func<FutClient, TResult> func, object requestDetails = null,
[CallerMemberName] string exceptionMessage = null)
{
try
{
if (LastException + new TimeSpan(0, 0, 30) < DateTime.Now)
{
TResult result = func(_futClient);
return result;
}
}
catch (Exception e)
{
Here I try to wrap and catch any exception thrown inside the Futclient, but instead I get a null exception in for example this line
return await Invoke(f => f.GetItemAsync(resourceId), resourceId);
And it seems its because my
TResult result = func(_futClient);
return result;
Is not run async and it just returns an empty response (All null values)
So I changed it to look like http://pastebin.com/pJkPr2xN
Code snippet:
public async Task<AuctionResponse> GetTradePileAsync()
{
return await NewInvoke<AuctionResponse>(new Task<object>(() => _futClient.GetTradePileAsync()));
}
public async Task<WatchlistResponse> GetWatchlistAsync()
{
return await NewInvoke<WatchlistResponse>(new Task<object>(() => _futClient.GetWatchlistAsync()));
}
public async Task<TResult> NewInvoke<TResult>(Task<object> taskToRun) where TResult : class
{
try
{
taskToRun.Start();
Task.WaitAll(taskToRun);
return taskToRun.Result as TResult;
}
catch (AggregateException ex)
{
//ex.InnerException
return null;
}
catch (Exception e)
{
return null;
}
}
But also here I get exceptions outside my invoke method
So how should I make this so it handles both Task and Task as a response type and takes x number of arguments
And it should then throw any exceptions inside the Invoke method so I only have one place where exceptions needs to be caught
You must await inside the try block before you return, otherwise the exception will not be cought by the catch. Your first attempt was actually closer to the correct way to do it.
public async Task<TResult> Invoke<TResult>(Func<FutClient, Task<TResult>> func, object requestDetails = null,
[CallerMemberName] string exceptionMessage = null)
{
try
{
if (LastException + new TimeSpan(0, 0, 30) < DateTime.Now)
{
TResult result = await func(_futClient).ConfigureAwait(false); //Here we both await for the result and set `ConfigureAwait` to false so we don't need to waist extra time trying to marshal back on to the original Synchronization context as we have no need for it for the rest of the method.
return result;
}
}
catch (Exception e)
{
//...
You then just use it like the following
public Task<AuctionResponse> PlaceBidAsync(AuctionInfo auctionInfo, uint bidAmount = 0)
{
return Invoke(f => f.PlaceBidAsync(auctionInfo, bidAmount), auctionInfo);
}
There is no need to await in this outer function, you can just directly return the Task returned by Invoke

Categories

Resources