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()
.
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!
Task. WhenAll doesn't start tasks. The general pattern is that methods that return a Task , return a hot Task .
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With