question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

TaskT_SyncAsync_Extensions.IterT is eager

See original GitHub issue

IEnumerable<Task<T>>.IterT extension method is eager. Is it expected? Is this related to #564?

This can lead to unintentional iteration (in some cases creation and start) of all tasks in a sequence. Here’s a small snippet that’d show an extreme case of the issue. In less extreme cases it may lead to uncontrolled parallelism which goes unnoticed.

public class Program
{
    public static void ConsoleLog(string str) => Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: {str}");

    public static IEnumerable<Task<int>> GetNumbersAsync()
    {
        int i = 0;
        while (true) // Mimic infinite (or just very long sequence of data)
            yield return GetNumberAsync();

        async Task<int> GetNumberAsync()
        {
            var value = Interlocked.Increment(ref i);
            if (value % 100_000 == 0) // I'm not printing every single task to avoid spamming the screenshot and to show how many tasks can be created in just a few seconds
                ConsoleLog($"Starting {nameof(GetNumberAsync)} Task #{value}");

            await Task.Delay(1000); // Mimic some network/disk IO
            return value;
        }
    }

    public static async Task Main(string[] args)
    {
        ConsoleLog("Starting the application");
        await GetNumbersAsync()
                .IterT(value =>
                {
                    ConsoleLog($"Value: {value}");
                    Thread.Sleep(500); // Mimic some CPU intensive work
                });
    }
}

image image

Because the enumeration of the GetNumbersAsync is eager it’ll create hundreds of thousands of tasks that start right away in the background. If GetNumbersAsync would query some service instead we would bomb the service with hundreds of thousands requests.

In this example application managed to spawn 3.2kk tasks while it was able to process only 10 values.

More information on the extension method: namespace: LanguageExt class: TaskT_SyncAsync_Extensions method signature: public static Task<Unit> IterT<A>(this IEnumerable<Task<A>> ma, Action<A> f)

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:15 (13 by maintainers)

github_iconTop GitHub Comments

1reaction
chyczewski-maciejcommented, Aug 20, 2019

@louthy, @gwintering, @bender2k14 Thanks for the effort you guys put into this issue (and the entire library - it rises C# to a new level ) 😃

0reactions
louthycommented, Aug 19, 2019

Is there a way to prevent other people from assuming it’d iterate elements of the sequence as they are needed

I’m not sure there’s any need. The behavior is a nested fold with a unit state:

    IEnumerable<Task<Foo>> tasks;
    tasks.Fold(unit, (_, t) => t.Fold(unit, f));

Iter is about side-effects, it’s unknown what those side-effects are going to be. If the tasks are truly using IO then who’s to say how much throughput any one machine could do in parallel? The .NET scheduler may not be as effective as you’d like in all situations, but IterT can’t make that judgement. It’s not waiting for results, it’s firing an forgetting - which is kind of implied by the fact that Iter returns Unit. And so it will try to generate as many tasks as it can and let the .NET scheduler deal with it.

If it did anything else then I’d argue it wouldn’t be iteration. It has no mandate to slow down, or do cunning stuff like wait for n tasks to complete before moving on.

WindowIter is explicit in its behaviour and I think that’s the correct approach.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Lazy and once-only C# async initialization
The problem with the async techniques just shown is that if the initialization fails, you are stuck with Task<T> that is in a...
Read more >
Asynchronous Programming - EF Core
The EF Core async extension methods are defined in the Microsoft.EntityFrameworkCore namespace. This namespace must be imported for the methods ...
Read more >
Async/Await: is it possible to start a Task on @MainActor ...
Hello,. I could never start a Task on the @MainActor in a synchronous way, even if the task is started from the main...
Read more >
How to call asynchronous method from synchronous ...
Run(async () => await MyAsyncMethod()); var result = task.WaitAndUnwrapException();. However, this solution requires a MyAsyncMethod that will ...
Read more >
Asynchronous I/O (asyncio) — SQLAlchemy 2.0 ...
Reference to the sync-style Engine this AsyncEngine proxies requests towards. This instance can be used as an event target. See also. Using events...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found