Как правильно вызвать последовательность задач?
От: mDmitriy Россия  
Дата: 13.09.17 08:40
Оценка:
Все привет!

При старте приложения надо запустить фоном выполнение нескольких тяжеловесных методов
Имеем:
var first = new Action(() => {});
var after1 = new Action(() => {});
var after2 = new Action(() => {});
...
var afterN = new Action(() => {});

Нужно дождаться выполнения метода first и запустить параллельно все остальные
Сейчас это работает так:
var _afters = 
{
   new Task(after1),
   new Task(after2),
   ...
   new Task(afterN),
}

var task = Task.Run(() => first).ContinueWith(t =>
                {
                    _afters.ForEach(a => a.Start());
                    Task.WaitAll(_afters);
                });

Можно ли написать как-то покрасивее?
Спасибо...
Re: Как правильно вызвать последовательность задач?
От: Nikolay_Ch Россия  
Дата: 13.09.17 08:54
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>Можно ли написать как-то покрасивее?

А покрасивее — это как? Что смущает?
Re: Как правильно вызвать последовательность задач?
От: _Raz_  
Дата: 13.09.17 09:01
Оценка: +1
Здравствуйте, mDmitriy, Вы писали:

D>Можно ли написать как-то покрасивее?


  await first();
  Parallel.Invoke(
    () => {},
    () => {},
    () => {},
  );
... << RSDN@Home 1.3.108 alpha 5 rev. 56>>
Re[2]: Как правильно вызвать последовательность задач?
От: mDmitriy Россия  
Дата: 13.09.17 09:13
Оценка:
Здравствуйте, Nikolay_Ch, Вы писали:
D>>Можно ли написать как-то покрасивее?
N_C>А покрасивее — это как? Что смущает?
в 1 оператор
и не стартовать задачи из массива руками
в примерах вроде можно стартовать 1 задачу, остальные как-то подтягиваются
а у меня так не получается
Re[2]: Как правильно вызвать последовательность задач?
От: Nikolay_Ch Россия  
Дата: 13.09.17 09:16
Оценка: 3 (1)
Здравствуйте, _Raz_, Вы писали:

_R_>Здравствуйте, mDmitriy, Вы писали:


D>>Можно ли написать как-то покрасивее?


_R_>
_R_>  await first();
_R_>  Parallel.Invoke(
_R_>    () => {},
_R_>    () => {},
_R_>    () => {},
_R_>  );
_R_>

Можно оставить и без await:
var _afters = new[] { after1, after2, after3, after4, after5 };
Task.Run(()=>first).ContinueWith((t)=>Parallel.Invoke(_afters));
Re[2]: Как правильно вызвать последовательность задач?
От: mDmitriy Россия  
Дата: 13.09.17 09:18
Оценка:
Здравствуйте, _Raz_, Вы писали:
_R_>
_R_>  await first();
_R_>  Parallel.Invoke(
_R_>    () => {},
_R_>    () => {},
_R_>    () => {},
_R_>  );

зачем await перед first()?
и мне нужно чтобы в Task все было
Re[2]: Как правильно вызвать последовательность задач?
От: _Raz_  
Дата: 13.09.17 09:18
Оценка:
    var task = Task.Run(() => {
        first();
        Parallel.Invoke(
        ...
        );
    });
... << RSDN@Home 1.3.108 alpha 5 rev. 56>>
Re[3]: Как правильно вызвать последовательность задач?
От: Nikolay_Ch Россия  
Дата: 13.09.17 09:19
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>в 1 оператор

В один оператор — это все равно будет набор операторов, умещенных в одну строку (см. мой пример)

D>в примерах вроде можно стартовать 1 задачу, остальные как-то подтягиваются

В каких примерах? Покажите. Что значит "как-то"? Примеры надо разбирать, чтобы понимать это "как-то"
Re[3]: Как правильно вызвать последовательность задач?
От: _Raz_  
Дата: 13.09.17 09:22
Оценка: +1
Здравствуйте, Nikolay_Ch, Вы писали:

N_C>Можно оставить и без await:


Можно, но тогда лучше с Then.
... << RSDN@Home 1.3.108 alpha 5 rev. 56>>
Re: Как правильно вызвать последовательность задач?
От: _Raz_  
Дата: 13.09.17 09:34
Оценка:
Здравствуйте, mDmitriy, Вы писали:

Еще вариант

    var afters =
    {
        new Task(after1, TaskCreationOptions.AttachedToParent),
        new Task(after2, TaskCreationOptions.AttachedToParent),
        ...
        new Task(afterN, TaskCreationOptions.AttachedToParent)
    }

    Task.Run(() =>
    {
        first();
        afters.ForEach(a => a.Start());
    })
    .Wait();
... << RSDN@Home 1.3.108 alpha 5 rev. 56>>
Re: Как правильно вызвать последовательность задач?
От: StatujaLeha на правах ИМХО
Дата: 13.09.17 10:05
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>Все привет!


D>При старте приложения надо запустить фоном выполнение нескольких тяжеловесных методов

            var first = new Action(() =>
                                   {
                                       Console.WriteLine("First!");
                                       Thread.Sleep(200);
                                   });
            var after1 = new Action(() => { Thread.Sleep(30); Console.WriteLine("1"); });
            var after2 = new Action(() => { Thread.Sleep(2000); Console.WriteLine("2"); });
            var after3 = new Action(() => { Thread.Sleep(10); Console.WriteLine("3"); });

            Task.Run(async () =>
                     {
                         first();
                         await Task.WhenAll(Task.Run(after1), Task.Run(after2), Task.Run(after3));
                     }).Wait();
Re: Как правильно вызвать последовательность задач?
От: karbofos42 Россия  
Дата: 13.09.17 12:16
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>Все привет!


D>При старте приложения надо запустить фоном выполнение нескольких тяжеловесных методов

D>Имеем:
D>
D>var first = new Action(() => {});
D>var after1 = new Action(() => {});
D>var after2 = new Action(() => {});
D>...
D>var afterN = new Action(() => {});
D>

D>Нужно дождаться выполнения метода first и запустить параллельно все остальные
D>Сейчас это работает так:
D>
D>var _afters = 
D>{
D>   new Task(after1),
D>   new Task(after2),
D>   ...
D>   new Task(afterN),
D>}

D>var task = Task.Run(() => first).ContinueWith(t =>
D>                {
D>                    _afters.ForEach(a => a.Start());
D>                    Task.WaitAll(_afters);
D>                });
D>

D>Можно ли написать как-то покрасивее?
D>Спасибо...

Вместо _afters завести ActionBlock?
Re[2]: Как правильно вызвать последовательность задач?
От: _Raz_  
Дата: 13.09.17 12:28
Оценка:
Здравствуйте, karbofos42, Вы писали:

K>Вместо _afters завести ActionBlock?


Разные же анонимные методы для задач, а ActionBlock работает только с одним. Да и ActionBlock переходит в ошибку после первого эксепшина и не выполняет оставшуюся очередь — подойдет ли это топик стартеру.
... << RSDN@Home 1.3.108 alpha 5 rev. 56>>
Re[2]: Как правильно вызвать последовательность задач?
От: mDmitriy Россия  
Дата: 13.09.17 12:33
Оценка:
Здравствуйте, karbofos42, Вы писали:
K>Вместо _afters завести ActionBlock?
Спасибо, подумаю в его сторону
Re[3]: Так подойдет, спасибо!
От: mDmitriy Россия  
Дата: 13.09.17 12:37
Оценка:
Здравствуйте, Nikolay_Ch, Вы писали:
N_C>
var _afters = new[] { after1, after2, after3, after4, after5 };
Task.Run(()=>first).ContinueWith((t)=>Parallel.Invoke(_afters));
Re[3]: Как правильно вызвать последовательность задач?
От: karbofos42 Россия  
Дата: 13.09.17 13:04
Оценка:
Здравствуйте, _Raz_, Вы писали:

_R_>Здравствуйте, karbofos42, Вы писали:


K>>Вместо _afters завести ActionBlock?


_R_>Разные же анонимные методы для задач, а ActionBlock работает только с одним. Да и ActionBlock переходит в ошибку после первого эксепшина и не выполняет оставшуюся очередь — подойдет ли это топик стартеру.


Ну, какую-нибудь такую штуку можно написать: ActionBlock<Action>((a) => a.Invoke()). Но для этого конечно Parallel.Invoke логичнее использовать.
Re[4]: Так подойдет, спасибо!
От: StatujaLeha на правах ИМХО
Дата: 14.09.17 09:39
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>var _afters = new[] { after1, after2, after3, after4, after5 };

D>Task.Run(()=>first).ContinueWith((t)=>Parallel.Invoke(_afters));[/cs]

Как я понял по описанию Parallel.Invoke, получится, что ContinueWith запускает Parallel.Invoke и ждет, пока он отработает(т.е. таска ContinueWith будет периодически получать время на выполнение, даже если не все задачи завершены).
Если же сделать как-нибудь через await
Task.Run(() => first()).ContinueWith(async (_) => await Task.WhenAll(Task.Run(after1), Task.Run(after2), Task.Run(after3)))

то таска ContinueWith запустит все задачи и в следующий раз будет разбужена только по завершении всех задач.
Re[5]: Так подойдет, спасибо!
От: Nikolay_Ch Россия  
Дата: 14.09.17 11:53
Оценка:
Здравствуйте, StatujaLeha, Вы писали:

SL>то таска ContinueWith запустит все задачи и в следующий раз будет разбужена только по завершении всех задач.

Э... Что? Первая таска завершится и после нее начнет выполнятся вторая таска. Вторая будет в состоянии ожидания, пока Parallel не завершится.
По-моему разницы быть не должно.
Re[6]: Так подойдет, спасибо!
От: StatujaLeha на правах ИМХО
Дата: 14.09.17 12:26
Оценка:
Здравствуйте, Nikolay_Ch, Вы писали:

N_C>Э... Что? Первая таска завершится и после нее начнет выполнятся вторая таска. Вторая будет в состоянии ожидания, пока Parallel не завершится.

N_C>По-моему разницы быть не должно.

Есть тредпул. Потоки из него обрабатывают таски.
Идеальный вариант:
1. Приходим в таску ContinueWith и запускаем все нужные таски after.
2. Ставим на паузу таску ContinueWith.
3. Ждем, пока все таски after завершатся.
4. Идем обратно в таску ContinueWith и завершаем ее.

Т.е. мы тратим на таску ContinueWith ресурсы только тогда, когда оно реально необходимо.
В примере через await оно так и должно работать.

А вот в варианте через Parallel это мне непонятно...
Пришли мы к вызову Parallel.Invoke, запустили все таски after.
И как тредпул теперь поймет, что до завершения всех тасков after не надо тратить вычислительные ресурсы на таску, из которой запущен Parallel.Invoke?
Re[7]: Так подойдет, спасибо!
От: Nikolay_Ch Россия  
Дата: 14.09.17 13:38
Оценка:
Здравствуйте, StatujaLeha, Вы писали:

SL>И как тредпул теперь поймет, что до завершения всех тасков after не надо тратить вычислительные ресурсы на таску, из которой запущен Parallel.Invoke?

Посмотрите в исходники.Net... Там есть несколько вариантов работы Invoke, в зависимости от количества переданных Action'ов... Один из которых WaitAll, другой — FastWaitAll...
Отредактировано 14.09.2017 13:39 Nikolay_Ch . Предыдущая версия . Еще …
Отредактировано 14.09.2017 13:39 Nikolay_Ch . Предыдущая версия .
Отредактировано 14.09.2017 13:38 Nikolay_Ch . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.