2

Consider this situation:

class Product { }

interface IWorker
{
    Task<Product> CreateProductAsync();
}

I am now given an IEnumerable<IWorker> workers and am supposed to create an IEnumerable<Product> from it that I have to pass to some other function that I cannot alter:

void CheckProducts(IEnumerable<Product> products);

This methods needs to have access to the entire IEnumerable<Product>. It is not possible to subdivide it and call CheckProducts on multiple subsets.

One obvious solution is this:

CheckProducts(workers.Select(worker => worker.CreateProductAsync().Result));

But this is blocking, of course, and hence it would only be my last resort. Syntactically, I need precisely this, just without blocking.

I cannot use await inside of the function I'm passing to Select() as I would have to mark it as async and that would require it to return a Task itself and I would have gained nothing. In the end I need an IEnumerable<Product> and not an IEnumerable<Task<Product>>.

It is important to know that the order of the workers creating their products does matter, their work must not overlap. Otherwise, I would do this:

async Task<IEnumerable<Product>> CreateProductsAsync(IEnumerable<IWorker> workers)
{
    var tasks = workers.Select(worker => worker.CreateProductAsync());
    return await Task.WhenAll(tasks);
}

But unfortunately, Task.WhenAll() executes some tasks in parallel while I need them executed sequentially.

Here is one possibility to implement it if I had an IReadOnlyList<IWorker> instead of an IEnumerable<IWorker>:

async Task<IEnumerable<Product>> CreateProductsAsync(IReadOnlyList<IWorker> workers)
{
    var resultList = new Product[workers.Count];
    for (int i = 0; i < resultList.Length; ++i)
        resultList[i] = await workers[i].CreateProductAsync();
    return resultList;
}

But I must deal with an IEnumerable and, even worse, it is usually quite huge, sometimes it is even unlimited, yielding workers forever. If I knew that its size was decent, I would just call ToArray() on it and use the method above.

The ultimate solution would be this:

async Task<IEnumerable<Product>> CreateProductsAsync(IEnumerable<IWorker> workers)
{
    foreach (var worker in workers)
        yield return await worker.CreateProductAsync();
}

But yield and await are incompatible as described in this answer. Looking at that answer, would that hypothetical IAsyncEnumerator help me here? Does something similar meanwhile exist in C#?

A summary of the issues I'm facing:

  • I have a potentially endless IEnumerable<IWorker>
  • I want to asynchronously call CreateProductAsync() on each of them in the same order as they are coming in
  • In the end I need an IEnumerable<Product>

A summary of what I already tried, but doesn't work:

  • I cannot use Task.WhenAll() because it executes tasks in parallel.
  • I cannot use ToArray() and process that array manually in a loop because my sequence is sometimes endless.
  • I cannot use yield return because it's incompatible with await.

Does anybody have a solution or workaround for me? Otherwise I will have to use that blocking code...

sebrockm
  • 5,733
  • 2
  • 16
  • 39
  • 1
    Your big mistake here, is you are talking about the ways you are trying to solve a problem, but don't explain enough about what you are trying to solve. – TheGeneral Jun 16 '18 at 00:35
  • @TheGeneral erm... almost every code snippet that I posted should explain what I am trying to solve, at least that was my intention. Can you tell me what exactly is unclear to you? Then I will try to clarify. Short summary: I want to transform `IEnumerable` to `IEnumerable` calling `CreateProductAsync()` on every worker without blocking. – sebrockm Jun 16 '18 at 00:42
  • Why can't you simply declare an async expression to the `Select (...)`? – JSteward Jun 16 '18 at 00:44
  • @JSteward Because the result of that `Select` would be `IEnumerable>`, but I need `IEnumerable`. async functions do return a task. – sebrockm Jun 16 '18 at 00:46
  • But the result of an `await WhenAll` will be what you need. – JSteward Jun 16 '18 at 00:47
  • @JSteward Precisely. But, as described, `WhenAll` does not guarantee any execution order. My tasks must not be executed in parallel. Actually the issue is, that one worker's work may depend on the previous worker's work being finished. – sebrockm Jun 16 '18 at 00:49
  • Ah I missed that part! Sounds like you could use TransformBlock and an AsObservable. – JSteward Jun 16 '18 at 00:54
  • I'll put a sample together and we'll see if it works for you. – JSteward Jun 16 '18 at 00:57
  • @JSteward great! Thanks for you efforts – sebrockm Jun 16 '18 at 00:57
  • Seems like your description has some conflicts, first you said: _This methods needs to check the **entire Product set as a whole**. It is not possible to subdivide the result._, then you talk about workers: _it is usually quite huge, sometimes it is even unlimited, **yielding workers forever**_ - Does this mean that in case of forever yielding workers `CheckProducts` cannot be executed because it need to check products as whole set? – Fabio Jun 16 '18 at 02:57
  • @Fabio Thanks for that hint. I realize that the wording "as a whole" is misleading. I updated my question, trying to be more precise. – sebrockm Jun 16 '18 at 13:23

9 Answers9

3

IEnumerator<T> is a synchronous interface, so blocking is unavoidable if CheckProducts enumerates the next product before the next worker has finished creating the product.

Nevertheless, you can achieve parallelism by creating products on another thread, adding them to a BlockingCollection<T>, and yielding them on the main thread:

static IEnumerable<Product> CreateProducts(IEnumerable<IWorker> workers)
{
    var products = new BlockingCollection<Product>(3);

    Task.Run(async () => // On the thread pool...
    {
        foreach (IWorker worker in workers)
        {
            Product product = await worker.CreateProductAsync(); // Create products serially.
            products.Add(product); // Enqueue the product, blocking if the queue is full.
        }

        products.CompleteAdding(); // Notify GetConsumingEnumerable that we're done.
    });

    return products.GetConsumingEnumerable();
}

To avoid unbounded memory consumption, you can optionally specify the capacity of the queue as a constructor argument to BlockingCollection<T>. I used 3 in the code above.

Michael Liu
  • 52,147
  • 13
  • 117
  • 150
  • This really looks like a thing... I will see if it works out, thanks! – sebrockm Jun 16 '18 at 10:32
  • I just tried it and it looks like working nicely! It still contains some blocking, but I realize that this cannot be completely avoided in my scenario. At least this solution provides some background task having the workers work asynchronously. One question though: Couln't this method just return the `BlockingCollection` directly rather than looping through it with `yield return`? – sebrockm Jun 16 '18 at 14:04
  • You're right; I've updated my answer to return `products.GetConsumingEnumerable()` (not just `products`, which doesn't wait for CompleteAdding to be called). Note that, unlike cwharris's answer, my code produces and consumes products in parallel, which may or may not be what you want. – Michael Liu Jun 16 '18 at 20:36
  • It seems like, so far, this is as close as it can get to what I want. Still not optimal, but maybe there is just no optimal solution for my problem... – sebrockm Jun 17 '18 at 14:27
  • @sebrockm asynchronous doesn’t mean “it happens while something else happens”. That’s what parallel means. Asynchronous means allowing the current thread to perform other work while waiting on the original work to finish. Multithreading is doing multiple things at the same time by utilizing multiple threads. It is important that you understand the difference. – cwharris Jun 17 '18 at 16:27
  • @MichaelLiu thanks for thinking out side the box on this one. It helped OP find a real solution to the problem he was having, rather than the requirements he was giving. – cwharris Jun 17 '18 at 16:29
  • @cwharris Thank you, I know the difference and I understand that this solution is not fully async as I wished it would be. However, this solution is only blocking one single task while this task asynchronously calls the workers. This is IMO still better than blocking on every `CreateProductAsync` call. Or do I miss something imortant? – sebrockm Jun 17 '18 at 17:30
  • You don’t understand what asynchronous means. There is nothing asynchronous about this solution. It’s a great solution for your problem, but it is not asynchronous. – cwharris Jun 17 '18 at 17:37
  • 1
    The part of this code that creates products—the `async` lambda—is asynchronous. The part that returns the products is synchronous. It's impossible to write asynchronous, *non-parallel* code using IEnumerable, which exposes only a synchronous API. – Michael Liu Jun 17 '18 at 22:13
2

The Situation:

Here you're saying you need to do this synchronously, because IEnumerable doesn't support async and the requirements are you need an IEnumerable<Product>.

I am now given an IEnumerable workers and am supposed to create an IEnumerable from it that I have to pass to some other function that I cannot alter:

Here you say the entire product set needs to be processed at the same time, presumably making a single call to void CheckProducts(IEnumerable<Product> products).

This methods needs to check the entire Product set as a whole. It is not possible to subdivide the result.

And here you say the enumerable can yield an indefinite number of items

But I must deal with an IEnumerable and, even worse, it is usually quite huge, sometimes it is even unlimited, yielding workers forever. If I knew that its size was decent, I would just call ToArray() on it and use the method above.

So lets put these together. You need to do asynchronous processing of an indefinite number of items within a synchronous environment and then evaluate the entire set as a whole... synchronously.

The Underlying Problems:

  • 1: To evaluate a set as a whole, it must be completely enumerated. To completely enumerate a set, it must be finite. Therefore it is impossible to evaluate an infinite set as a whole.
  • 2: Switching back and forth between sync and async forces the async code to run synchronously. that might be ok from a requirements perspective, but from a technical perspective it can cause deadlocks (maybe unavoidable, I don't know. Look that up. I'm not the expert).

Possible Solutions to Problem 1:

  • 1: Force the source to be an ICollection<T> instead of IEnumerable<T>. This enforces finiteness.
  • 2: Alter the CheckProducts algorithm to process iteratively, potentially yielding intermediary results while still maintaining an ongoing aggregation internally.

Possible Solutions to Problem 2:

  • 1: Make the CheckProducts method asynchronous.
  • 2: Make the CreateProduct... method synchronous.

Bottom Line

You can't do what you're asking how you're asking, and it sounds like someone else is dictating your requirements. They need to change some of the requirements, because what they're asking for is (and I really hate using this word) impossible. Is it possible you have misinterpreted some of the requirements?

cwharris
  • 17,835
  • 4
  • 44
  • 64
  • Regarding Problem 1: I realize that my wording "as a whole" might be misleading. What I meant: `CheckProducts` needs to evaluate the products in context of each other. One possible scenario could be e.g. that one product's correctness can be determined only bases on the previous two products. It does not necessarily mean that each product's correctness depents on the entire set (which would be impossible for an infinite set, of course) but it may depend on *some* other products. In the end I simply don't know how it works and thus I cannot provide it with small chunks of products only – sebrockm Jun 16 '18 at 10:15
  • Regarding Problem 2: As far as I can tell, this is not much of a concern. Maybe there is even an async analogous of CheckProducts... The issue is only that in either case it requires `IEnumerable`. – sebrockm Jun 16 '18 at 10:20
  • Your second solution of Problem 1 sound like an idea. Is it meant to be similar to the Thread Pool Solution in John Wu's answer? – sebrockm Jun 16 '18 at 10:22
  • Sorry, I meant alter the CheckProducts algorithm. – cwharris Jun 16 '18 at 12:18
  • That being said, I don’t think this is the answer. I think my other answer is what you’re looking for. – cwharris Jun 16 '18 at 12:19
  • You misinterpreted the requirements. The solution you selected is not asynchronous - it’s parallelized via multithreading. That’s entirely different. – cwharris Jun 17 '18 at 16:23
1

From your requirements I can put together the following:

1) Workers processed in order

2) Open to receive new Workers at any time

So using the fact that a dataflow TransformBlock has a built in queue and processes items in order. Now we can accept Workers from the producer at any time.

Next we make the result of the TransformBlockobservale so that the consumer can consume Products on demand.

Made some quick changes and started the consumer portion. This simply takes the observable produced by the Transformer and maps it to an enumerable that yields each product. For background here is the ToEnumerable().

The ToEnumerator operator returns an enumerator from an observable sequence. The enumerator will yield each item in the sequence as it is produced

Source

using System;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;

namespace ClassLibrary1
{
    public class WorkerProducer
    {
        public async Task ProduceWorker()
        {
            //await ProductTransformer_Transformer.SendAsync(new Worker())
        }
    }

    public class ProductTransformer
    {
        public IObservable<Product> Products { get; private set; }
        public TransformBlock<Worker, Product> Transformer { get; private set; }

        private Task<Product> CreateProductAsync(Worker worker) => Task.FromResult(new Product());

        public ProductTransformer()
        {
            Transformer = new TransformBlock<Worker, Product>(wrk => CreateProductAsync(wrk));
            Products = Transformer.AsObservable();
        }
    }

    public class ProductConsumer
    {
        private ThirdParty ThirdParty { get; set; } = new ThirdParty();
        private ProductTransformer Transformer { get; set; }

        public ProductConsumer()
        {
            ThirdParty.CheckProducts(Transformer.Products.ToEnumerable());  
        }

    public class Worker { }
    public class Product { }

    public class ThirdParty
    {
        public void CheckProducts(IEnumerable<Product> products)
        {
        }
    }
}
JSteward
  • 6,833
  • 2
  • 21
  • 30
  • I'm not sure if I understand how to use this... Could you explain it a little bit more? – sebrockm Jun 16 '18 at 01:19
  • @sebrockm added some details – JSteward Jun 16 '18 at 01:28
  • 1
    I will go for some sleep now, it's almost 4pm here :D Maybe tomorrow I will be able to understand this. Thank you for the answer anyway! – sebrockm Jun 16 '18 at 01:45
  • @sebrockm just to note an observable can calculate a variety of operations or you can simply do `Take(3) and do what you need to do with those 3 products – JSteward Jun 16 '18 at 01:49
  • Observable is push-based, and therefore doesn't match the pull-based requirements. All of these will be processed as soon as possible, whereas an Enumerable will be processed one at a time. I don't know, maybe that's what OP wants - batch processing. – cwharris Jun 16 '18 at 03:30
  • @cwharris I did intend this as push based. I assumed a `Worker` being produced would cause the `Product` to get pushed to a consumer. I agree though it's not completely clear what the OP needs, at least there's 6 answers to choose from now. – JSteward Jun 16 '18 at 03:34
  • In the question, OP states he needs them to be processed one after the other. He says a lot of other stuff too, some of which is contradictory. – cwharris Jun 16 '18 at 04:02
  • @sebrockm started un-tested work on the Consumer side just as quick example. – JSteward Jun 16 '18 at 04:39
1

Two ideas for you OP

Multiple call solution

If you are allowed to call CheckProducts more than once, you could simply do this:

foreach (var worker in workers)
{
    var product = await worker.CreateProductAsync();
    CheckProducts(new [] { product } );
}

If it adds value, I'm pretty sure you could work out a way to do it in batches of, say, 100 at a time, too.

Thread pool solution

If you are not allowed to call CheckProducts more than once, and not allowed to modify CheckProducts, there is no way to force it to yield control and allow other continuations to run. So no matter what you do, you cannot force asynchronousness into the IEnumerable that you pass to it, not just because of the compiler checking, but because it would probably deadlock.

So here is a thread pool solution. The idea is to create one separate thread to process the products in series; the processor is async, so a call to CreateProductAsync() will still yield control to anything else that has been posted to the synchronization context, as needed. However it can't magically force CheckProduct to give up control, so there is still some possibility that it will block occasionally if it is able to check products faster than they are created. In my example I'm using Monitor.Wait() so the O/S won't schedule the thread until there is something waiting for it. You'll still be using up a thread resource while it blocks, but at least you won't be wasting CPU time in a busy-wait loop.

public static IEnumerable<Product> CreateProducts(IEnumerable<Worker> workers)
{
    var queue = new ConcurrentQueue<Product>();
    var task = Task.Run(() => ConvertProducts(workers.GetEnumerator(), queue));
    while (true)
    {
        while (queue.Count > 0)
        {
            Product product;
            var ok = queue.TryDequeue(out product);
            if (ok) yield return product;
        }
        if (task.IsCompleted && queue.Count == 0) yield break;
        Monitor.Wait(queue, 1000);
    }
}

private static async Task ConvertProducts(IEnumerator<Worker> input, ConcurrentQueue<Product> output)
{
    while (input.MoveNext())
    {
        var current = input.Current;
        var product = await current.CreateProductAsync();
        output.Enqueue(product);
        Monitor.Pulse(output);
    }
}
John Wu
  • 50,556
  • 8
  • 44
  • 80
  • The multiple call solution is not an option, unfortunately. But I like that idea. I seems to be similar to Micheal Liu's answer – sebrockm Jun 16 '18 at 10:37
0

Unless I misunterstood something, I don't see why you don't simply do it like this:

var productList = new List<Product>(workers.Count())
foreach(var worker in workers)
{
    productList.Add(await worker.CreateProductAsync());
}
CheckProducts(productList);

What about if you simply keep clearing a List of size 1?

var productList = new List<Product>(1);
var checkTask = Task.CompletedTask;
foreach(var worker in workers)
{
    await checkTask;
    productList.Clear();
    productList.Add(await worker.CreateProductAsync());
    checkTask = Task.Run(CheckProducts(productList));
}
await checkTask;
Janilson
  • 1,042
  • 1
  • 9
  • 23
  • Thanks for your answer, but you are indeed missing something: `workers.Count()` may be infinity in some cases, e.g. created by an endless loop of `yield return new Worker()` – sebrockm Jun 16 '18 at 01:03
  • If I understand your solution correctly, you are suggesting to only check one product at a time? The idea is good, but the products depend on each other so that they can only be checked in a batch. I will clarify that in the question. – sebrockm Jun 16 '18 at 01:22
  • @sebrockm That seems to go against what you've said in your other comment. If there's the possibility of workers being generated in an infinite loop then you'll have an infinite loop of products being generated as well, which means you won't be able to check all of them together. – Janilson Jun 16 '18 at 01:31
  • An easy, artificial example: Consider the Products to be numbers. Then there might be the requirement that e.g. each number must be the sum of the previous three numbers. Thus the check function needs the entire streem of numbers to check that all meet that requirement. This is btw the same reason why the workers cannot work in parallel. – sebrockm Jun 16 '18 at 01:36
  • @sebrockm According to yourself : "This methods needs to check the entire Product set as a whole. It is not possible to subdivide the result.". In the situation where workers is endleslly generated by an infinite yield return loop, you'll never have the entire set of products, as it will be infinite as well. If workers won't be infinite, I believe my first answer solves your issue. If workers is generated by an infinite loop with some break condition, just remove workers.Count() from the List constructor and the first answer should still work. – Janilson Jun 16 '18 at 01:43
  • @sebrockm Maybe showing how workers is created might help – Janilson Jun 16 '18 at 01:46
  • The point is: `CheckProducts` is in reality a library function that I have not the slightest idea of how it is implemented. The same goes for the origin of the workers. I have no idea. I just know the interfaces and I am supposed to implement some function that by interface-definition takes a `IEnumerable` and shall per documentation forward the worker's results as an `IEnumerable` to that CheckProducts function. The actual names are not Worker, Product, and CheckProducts, but I called them like that for the sake of an example. – sebrockm Jun 16 '18 at 02:00
0

You can use Task.WhenAll, but instead of returning result of Task.WhenAll, return collection of tasks transformed to the collection of results.

async Task<IEnumerable<Product>> CreateProductsAsync(IEnumerable<IWorker> workers)
{
    var tasks = workers.Select(worker => worker.CreateProductAsync()).ToList();
    await Task.WhenAll(tasks);

    return tasks.Select(task => task.Result);
}

Order of tasks will be persisted.

And seems like should be ok to go with just return await Task.WhenAll()
From docs of Task.WhenAll Method (IEnumerable>)

The Task.Result property of the returned task will be set to an array containing all of the results of the supplied tasks in the same order as they were provided...

If workers need to be executed one by one in the order they were created and based on requirement that another function need whole set of workers results

async Task<IEnumerable<Product>> CreateProductsAsync(IEnumerable<IWorker> workers)
{
    var products = new List<product>();
    foreach (var worker in workers)
    {
        product = await worker.CreateProductAsync();
        products.Add(product);
    }

    return products;
}
Fabio
  • 31,528
  • 4
  • 33
  • 72
  • I'm sorry if my wording was misleading. The requirement not only is that the result has the same order (which `Task.WhenAll` guarantees), but also that e.g. worker2 must not start before worker1 is done because he his work may depend on worker1's output – sebrockm Jun 16 '18 at 10:35
0

You can do this now with async, IEnumerable and LINQ but every method in the chain after the async would be a Task<T>, and you need to use something like await Task.WhenAll at the end. You can use async lambdas in the LINQ methods, which return Task<T>. You don't need to wait synchronously in these.

The Select will start your tasks sequentially i.e. they won't even exist as tasks until the select enumerates each one, and won't keep going after you stop enumerating. You could also run your own foreach over the enumerable of tasks if you want to await them all individually.

You can break out of this like any other foreach without it starting all of them, so this will also work on an infinite enumerable.

public async Task Main()
{
    // This async method call could also be an async lambda
    foreach (var task in GetTasks())
    {
        var result = await task;
        Console.WriteLine($"Result is {result}");
        if (result > 5) break;
    }
}

private IEnumerable<Task<int>> GetTasks()
{
    return GetNumbers().Select(WaitAndDoubleAsync);
}

private async Task<int> WaitAndDoubleAsync(int i)
{
    Console.WriteLine($"Waiting {i} seconds asynchronously");
    await Task.Delay(TimeSpan.FromSeconds(i));
    return i * 2;
}

/// Keeps yielding numbers
private IEnumerable<int> GetNumbers()
{
    var i = 0;
    while (true) yield return i++;
}

Outputs, the following, then stops:

Waiting 0 seconds asynchronously
Result is 0
Waiting 1 seconds asynchronously
Result is 2
Waiting 2 seconds asynchronously
Result is 4
Waiting 3 seconds asynchronously
Result is 6

The important thing is that you can't mix yield and await in the same method, but you can yield Tasks returned from a method that uses await absolutely fine, so you can use them together just by splitting them into separate methods. Select is already a method that uses yield, so you may not need to write your own method for this.

In your post you were looking for a Task<IEnumerable<Product>>, but what you can actually use is a IEnumerable<Task<Product>>.

You can go even further with this e.g. if you had something like a REST API where one resource can have links to other resources, like if you just wanted to get a list of users of a group, but stop when you found the user you were interested in:

public async Task<IEnumerable<Task<User>>> GetUserTasksAsync(int groupId)
{
    var group = await GetGroupAsync(groupId);
    return group.UserIds.Select(GetUserAsync);
}

foreach (var task in await GetUserTasksAsync(1))
{
    var user = await task;
    ...
}
George Helyar
  • 4,319
  • 1
  • 22
  • 20
  • Please read my question carefully again. I explicitly stated that `Task.WhenAll` is not an option as I need my tasks to be executed sequentially, one after another. Unless you are aware of some version of `Task.WhenAll` that executes the task sequentially, this does not fit my needs. – sebrockm Jun 16 '18 at 13:09
  • The `Select` will start your tasks sequentially i.e. they won't even exist as tasks until the select enumerates them. `Task.WhenAll` is just a way to wait for them all to complete asynchronously. It pulls multiple `Task`s off the enumerable and executes in parallel, which is usually good for async code. You could also run your own `foreach` over the enumerable of tasks if you want to await them all individually. `foreach(var task in tasks) {var result = await task; ... }`. Can you cancel the downvote now? – George Helyar Jun 17 '18 at 13:53
  • You are right with everythin you are saying. But still you haven't studied my question carefully enough (that may be partially my fault as I have to admit that it is quite complicated). I say "It is important to know that the order of the workers creating their products does matter, **their work must not overlap**." Parallel execution is not applicable in my situation. I wish it was, but it isn't... I will update my question to point that out even more. – sebrockm Jun 17 '18 at 14:30
  • I have updated my answer. It does not operate in parallel. Each time the loop iterates, the task is created and starts. In the body of each pass, `await` waits for it to finish. The next task has not been created at this point, so there is no overlap. New language features are largely just syntactic sugar. You can do this now, you don't have to wait. – George Helyar Jun 17 '18 at 14:43
  • I've added more detail to the answer. I believe it fully answers your question now, or at the very least does not deserve a downvote. – George Helyar Jun 17 '18 at 15:14
  • Thank for your efforts! But I don't see how this helps me in getting one single `IEnumerable` from one single `IEnumerable`. Could you work that out a little more? – sebrockm Jun 17 '18 at 19:25
0

There is no solution to your problem. You can't transform a deferred IEnumerable<Task<Product>> to a deferred IEnumerable<Product>, such that the consuming thread will not get blocked while enumerating the IEnumerable<Product>. The IEnumerable<T> is a synchronous interface. It returns an enumerator with a synchronous MoveNext method. The MoveNext returns bool, which is not an awaitable type. An asynchronous interface IAsyncEnumerable<T> exists, whose enumerator has an asynchronous MoveNextAsync method, with a return type of ValueTask<bool>. But you have explicitly said that you can't change the consuming method, so you are stuck with the IEnumerable<T> interface. No solution then.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
-3

try

workers.ForEach(async wrkr => 
{
    var prdlist = await wrkr.CreateProductAsync();
    //Remaing tasks....
});
Lindsay
  • 575
  • 8
  • 18
  • There is no `ForEach` extension method on `IEnumerable` that I am aware of. There is a `ForAll` on `ParallelQuery`, but parallel creation of the products is not possible as I said – sebrockm Jun 16 '18 at 01:42
  • @sebrockm, good point - try casting to a list first. https://stackoverflow.com/questions/200574/linq-equivalent-of-foreach-for-ienumerablet – Lindsay Jun 16 '18 at 01:45
  • Ah I see, it's on `List`. Anyway, casting to a list is impossible as it may be of unlimited size, as mentioned several times. Also, as you declared your lambda async, it's result will be a `Task`, but I desperately need `Product`! – sebrockm Jun 16 '18 at 02:06
  • morelinq has a `ForEach` [extension method](https://github.com/morelinq/MoreLINQ#foreach). – Kenneth K. Jun 16 '18 at 02:35
  • Select and ForEach are entirely different things. One enumerates, the other does not. – cwharris Jun 16 '18 at 03:26