Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create a Task list, with tasks without executing

I have an async method

private async Task DoSomething(CancellationToken token)

a list of Tasks

private List<Task> workers = new List<Task>();

and I have to create N threads that runs that method

public void CreateThreads(int n)
{
    tokenSource = new CancellationTokenSource();
    token = tokenSource.Token;
    for (int i = 0; i < n; i++)
    {
        workers.Add(DoSomething(token));
    }
}

but the problem is that those have to run at a given time

public async Task StartAllWorkers()
{
    if (workers.Count > 0)
    {
        try
        {
            while (workers.Count > 0)
            {
                Task finishedWorker = await Task.WhenAny(workers.ToArray());
                workers.Remove(finishedWorker);
                finishedWorker.Dispose();
            }
            if (workers.Count == 0)
            {
                tokenSource = null;
            }
        }
        catch (OperationCanceledException)
        {
            throw;
        }
    }
}

but actually they run when i call the CreateThreads Method (before the StartAllWorkers). I searched for keywords and problems like mine but couldn't find anything about stopping the task from running. I've tried a lot of different aproaches but anything that could solve my problem entirely. For example, moving the code from DoSomething into a workers.Add(new Task(async () => { }, token)); would run the StartAllWorkers(), but the threads will never actually start.

There is another method for calling the tokenSource.Cancel().

like image 872
Ernesto Araya Eguren Avatar asked Oct 27 '13 09:10

Ernesto Araya Eguren


People also ask

What is the difference between task run and task factory StartNew?

Task. Run(action) internally uses the default TaskScheduler , which means it always offloads a task to the thread pool. StartNew(action) , on the other hand, uses the scheduler of the current thread which may not use thread pool at all!

Does task WhenAny start the tasks?

Task. WhenAll doesn't start tasks. The general pattern is that methods that return a Task , return a hot Task .


1 Answers

You can use TaskCompletionSource<T> as a one-time "signal" to asynchronous methods.

So you'd create it like this:

private TaskCompletionSource<object> _tcs;
public void CreateThreads(int n)
{
    _tcs = new TaskCompletionSource<object>();
    tokenSource = new CancellationTokenSource();
    token = tokenSource.Token;
    for (int i = 0; i < n; i++)
    {
        workers.Add(DoSomething(_tcs.Task, token));
    }
}

Then when you're ready to start the tasks, just complete the "start" signal task:

public Task StartAllWorkers()
{
    _tcs.TrySetCompleted(null);
    return Task.WhenAll(workers);
}

(The StartAllWorkers method above has slightly different semantics than your original method: your original method would throw a cancellation exception as soon as the first task canceled; this one will wait until all the methods complete and then throw a cancellation exception)

Then your DoSomething just has to honor the "start signal":

private static async Task DoSomething(Task start, CancellationToken token)
{
    await start;
    ... // rest of your code
}
like image 65
Stephen Cleary Avatar answered Nov 10 '22 10:11

Stephen Cleary