Здравствуйте, Cynic, Вы писали:
C>Попробовал сделать как у Вас:
Не, это совсем не как у меня
Я завтра с утра напишу подробней, коротко: методы Task.Delay(), Task.Start() etc никак не влияют на текущий поток, они запускают новую задачу. Чтобы получить результат задачи — или task.Wait() / task.Result (блокирующее ожидание), или await.
Тут я не подумал конечно, заменил на Thread.Sleep. Асинхронным метод делать не хочу, его задача возвращать таски. Мои таски не должны возвращать значение (void), т.к. предполагается что они изменяют поля класса в котором они созданы.
S>Если метод асинхронный — он должен возвращать Task, чтобы было его дожидаться, например S>
C>Тут я не подумал конечно, заменил на Thread.Sleep. Асинхронным метод делать не хочу, его задача возвращать таски. Мои таски не должны возвращать значение (void), т.к. предполагается что они изменяют поля класса в котором они созданы.
Обычно все методы, возвращающие Task именно что асинхронные и возвращают hot task (уже работающую задачу). Проверка аргументов и подготовка запуска делаются конечно синхронно, а вот остальная часть обычно выполняется в фоне.
Thread.Sleep и прочие ожидающие блокировки плохо сказываются на масштабируемости тасков. Если иначе никак — можно оставить, но пометить как проблемное место. (Я обычно такие места размечаю как // TOFIX: comment )
C>Вы имеете ввиду, что по тому как я использовал это метод в Main он должен быть асинхронным? Но он же работает, что плохого в такой реализации?
То, что у вас нет способа дождаться завершения операции и узнать о её результате (exception — тоже результат). В итоге приложение будет или внезапно падать, или молча проглатывать исключения (зависит от версии фреймворка и <ThrowUnobservedTaskExceptions/> (детали).
Но это относится не только к async void, речь про любой метод, который запускает задачу и не получает её значение сам (или не отдаёт таск наружу, чтобы с ней возился вызывающий).
Разумеется, есть исключения (async event handler — одно из них), в общем случае так делать не надо.
C>Кстати в вашем примере я заметил, что вы объявляете метод как
static async Task Run(CancellationToken ct)
C>... но при этом в самом методе Run нет ни одного return Так и должно быть?
Да, если возвращается Task, а не Task<T>, компилятор сам всё разрулит.d>
C>В общем вот так всё работает:
Угу. Единственно, мне очень не нравится
TimeEngine.Add(this);
в конструкторе.
В общем случае это очень, очень, очень плохая практика. Код снаружи получает доступ к объекту до того, как отработают все конструкторы (не забываем про наследников),
static void Do(A a) { a.DoSmth(); }
class A
{
public A()
{
Do(this);
}
public virtual void DoSmth()
{
}
}
class B : A
{
private readonly string message;
public B()
{
this.message = "Hello";
}
public override void DoSmth()
{
base.DoSmth();
Console.WriteLine("Message: {0}, length: {1}", message, message.Length);
}
}
public static void Main(string[] args)
{
var x = new B();
Console.WriteLine("{0,5}",2);
}
обычно побочные эффекты выносят из конструктора, т.е. код будет выглядеть как-то так:
for (int i = 0; i < _taskCount; i++)
{
TimeEngine.Run(new Worker(i.ToString(), _rnd.Next(500, 1500));
}
Здравствуйте, Sinix, Вы писали:
S>Обычно все методы, возвращающие Task именно что асинхронные и возвращают hot task (уже работающую задачу). Проверка аргументов и подготовка запуска делаются конечно синхронно, а вот остальная часть обычно выполняется в фоне.
Короче понял я в чём проблема. Во всех примерах которые я видел обычно используется dotNET'овский метод который уже асинхронный (например Task.Delay), а в моём случае нужно создавать класс с нуля. И почему то ни в одной статье которую я читал, явного примера на этот счёт не было. Зато студия подсказала — если метод async, то либо просто await, либо await Task.Run() в теле метода должны быть. В общем вот так вот правильно:
public async Task GetWorker()
{
await Task.Run(() =>
{
Console.WriteLine("Worker {0} start", _name);
Thread.Sleep(_delay); // Это только для демонстрации того как превратить синхронный код в асинхронный!
Console.WriteLine("Worker {0} end", _name);
});
}
S>>В конкретно этом случае достаточно return Task.Run(), async/await тут не нужен. C>Да почему блин
А потому что ты сразу изучаешь и async/await, и таски. Они у тебя как одно целое воспринимаются, всё только запутывается. Сорри за диагноз по фотографии, легко могу ошибаться
Мне легко было, когда изучал — await ещё не было. А вот сколько помогал другим разобраться, всегда начинали с разделения мух и котлет.
твой код — это сокращённый способ записать вот это:
public async Task GetWorker()
{
var task = Task.Run(() => // (1)
{
Console.WriteLine("Worker {0} start", _name);
Thread.Sleep(_delay); // Это только для демонстрации того как превратить синхронный код в асинхронный!
Console.WriteLine("Worker {0} end", _name);
});
await task; // (2)
}
В (1) ты запускаешь задачу на выполнение (тело задачи выполняется асинхронно). В (2) — после завершения задачи сразу выходишь из метода.
Получается, код эквивалентен return Task.Run(...).