Как закрыть все таски если хотя бы одна закончилась
Есть decimal параметр. Предположим он равен 10. Есть Task которая раз в 100ms уменьшает его на 0.1. Как только параметр становится равен 1, таска должна завершится и больше параметр не уменьшать. Работает без проблем если Task только одна. Но если их 2, 3, 100... то параметр в итоге станет меньше 1. Я пробую использовать CancellationToken что бы завершить все таски, но результат всё такой же. Мой код:
class Program
{
static decimal param = 108;
static CancellationTokenSource cancelTokenSource = new CancellationTokenSource();
static CancellationToken token;
static void Main(string[] args)
{
int tasksCount = 16;
token = cancelTokenSource.Token;
Console.WriteLine("Start Param = {0}", param);
Console.WriteLine("Tasks Count = {0}", tasksCount);
var tasksList = new List<Task>();
for (var i = 0; i < tasksCount; i++)
{
Task task = new Task(Decrementation, token);
tasksList.Add(task);
}
tasksList.ForEach(x => x.Start());
Task.WaitAny(tasksList.ToArray());
Console.WriteLine("Result = {0}", param);
Console.Read();
}
private static void Decrementation()
{
while (true)
{
if (token.IsCancellationRequested)
{
break;
}
if (CanTakeMore())
{
Task.Delay(100);
param = param - 0.1m;
}
else
{
cancelTokenSource.Cancel();
return;
}
}
}
private static bool CanTakeMore()
{
if (param > 1)
{
return true;
}
else
{
return false;
}
}
}
Вывод бывает разный, но он всегда меньше 1.
Что я делаю не так?
Ответы (1 шт):
Вывод бывает разный
Потому что этот код не является потокобезопасным, вычитания и проверки значения надо делать под локом.
Task.Delayне работает безawait- конструктор
Taskне стоит использовать, совсем, это устаревший метод создания тасок CancellationTokenSourceэтоIDisposable, надо использоватьusing- избегайте глобальных ресурсов, полей и свойств в многопоточке, используйте аргументы методов и возвращаемые значения
- использовать синхронные комбинаторы
WaitAnyиWaitAllтоже не стоит, есть асинхронные
Давайте добавим немного потокобезопасности. Если все сделать правильно, даже токен отмены не нужен, незачем масло масляное устраивать, нсли все методы сами безопасно завершатся.
class Program
{
static decimal param = 108;
static readonly object syncRoot = new object();
static asynс Task Main(string[] args)
{
int tasksCount = 16;
Console.WriteLine($"Start Param = {param}");
Console.WriteLine($"Tasks Count = {tasksCount}");
var tasks = new List<Task>();
for (int i = 0; i < tasksCount; i++)
{
Task task = Task.Run(Decrementation);
tasks.Add(task);
}
await Task.WhenAll(tasks);
Console.WriteLine($"Result = {param}");
Console.ReadKey();
}
private static void Decrementation()
{
while (true)
{
lock (syncRoot)
{
if (CanTakeMore())
param -= 0.1m;
else
return;
}
Thread.Sleep(100); // здесь бы имел смысл токен отмены и await Task.Delay, если бы ожидание было длительным
// напишите коммент, нсли надо показать асинхронный код с отменой
}
}
private static bool CanTakeMore()
{
return param > 1;
}
}
