Re[5]: Красивое решение для задачи с потоками
От: Sinix  
Дата: 13.03.15 07:21
Оценка: +13 -1 :)
Здравствуйте, mapnik, Вы писали:

M>Пока космические корабли с софтом на java бороздят просторы вселенной и Марса как минимум 10 лет, MS внедряет инновационные "таски" и async/await. Видимо с помощью "программистов шарепоинт".

У btn1 появился конкурент

Это тематический раздел. Для посраться и показать себя красивого есть КСВ и мусорные разделы. Если не можете общаться без набросов — проходите мимо пожалуйста.
Re[7]: Красивое решение для задачи с потоками
От: Sinix  
Дата: 13.03.15 08:04
Оценка: 144 (5) +8
Здравствуйте, mapnik, Вы писали:


M>Да вы что? И что теперь в тематических разделах на вопросы по синхронизации потоков модно отвечать — "а иди как ты учи TPL", особенно когда человек пишет "Производительность решения очень важна!".


Я ещё раз попытаюсь, ок?

1. Вы не знаете про таски вообще ничего. Иначе не писали бы про производительность, инновационность тасков и про мёртвую технологию. Все три утверждения — мимо.

2. Вам топик интересен именно на предмет посраться. Вот это

что там еще у MS инновационного?
...
космические корабли с софтом на java бороздят просторы вселенной и Марса как минимум 10 лет, MS внедряет инновационные "таски" и async/await. Видимо с помощью "программистов шарепоинт"

не я писал.

3. TPL тут именно что правильное решение. Изучить недолго, всяко быстрее, чем изобрести свой велосипед с похожими характеристиками.


Если есть аргументированные возражения — велкам. Если нет — я пас.
Re: Красивое решение для задачи с потоками
От: nikov США http://www.linkedin.com/in/nikov
Дата: 13.03.15 04:49
Оценка: 11 (3) +7 -1
Здравствуйте, Cynic, Вы писали:

C>Не устраивают они меня потому, что пока нужно только три "вычислительных" потока с этим можно жить, а представьте во что это превратиться если их будет пару десятков.

C>Поэтому решил спросить Вас, есть ли способ более красиво решить требуемую задачу?

Советую забыть про потоки и учить таски и TPL.
Re[2]: Красивое решение для задачи с потоками
От: mapnik США http://www.hooli.xyz/
Дата: 13.03.15 06:39
Оценка: -7 :)
Здравствуйте, nikov, Вы писали:

N>Советую забыть про потоки и учить таски и TPL.


Отличный совет! А где же совет учить Windows Longhorn, ObjectSpaces и WinFS. И что там еще у MS инновационного?
Re[4]: Красивое решение для задачи с потоками
От: mapnik США http://www.hooli.xyz/
Дата: 13.03.15 07:04
Оценка: -7
Здравствуйте, Sinix, Вы писали:

S>Таски были "инновационными" в 2007м, если чо.


Вот это новость!
Пока космические корабли с софтом на java бороздят просторы вселенной и Марса как минимум 10 лет, MS внедряет инновационные "таски" и async/await. Видимо с помощью "программистов шарепоинт".
Re[6]: Красивое решение для задачи с потоками
От: mapnik США http://www.hooli.xyz/
Дата: 13.03.15 07:24
Оценка: -7
Здравствуйте, Sinix, Вы писали:

S>Это тематический раздел. Для посраться и показать себя красивого есть КСВ и мусорные разделы. Если не можете общаться без набросов — проходите мимо пожалуйста.


Да вы что? И что теперь в тематических разделах на вопросы по синхронизации потоков модно отвечать — "а иди как ты учи TPL", особенно когда человек пишет "Производительность решения очень важна!".
Я бы за такой ответ сразу банил, если совсем уже грубо
Re[5]: Красивое решение для задачи с потоками
От: Sinix  
Дата: 14.03.15 13:43
Оценка: 81 (4) +2
Здравствуйте, Cynic, Вы писали:

C>Каким образом удалось достигнуть такой производительности?

Переписыванием ThreadPool (шедулер по умолчанию работает поверх него), затем — перепиливанием внутренностей самих тасков (второе помогло куда больше, но и поломало пару мелочей).

Если коротко, то
1. Задачи не требуют переключения контекста.
2. Используются неблокирующие ожидания (например, на IOCP). Если задача ждёт чего-то, то она не блокирует поток. Пул может запустить следующую задачу — время CPU используется гораздо эффективней.
3. Начиная с 4.5 таски здорово оптимизированы под код в CPS-style (читай, с использованием await).

Если интересует реализация с точки зрения архитектуры — см раздел Decentralized Scheduling Techniques

Разумеется, таски НЕ улучшат производительность, если весь ваш код утыкается в любое другое горлышко: IO, CPU, сеть, свои взаимоблокировки и тд и тп. Кроме того, добавление долгих задач может испортить общую картину из-за исчерпания пула (чинится указанием TaskCreationOption.LongRunning). Потому что

aParllel prgoamrmnig s i ahdr

(с) Gastón C. Hillar

Но с самым популярным — блокировками на ожидание и неправильным использованием потоков и примитивов синхронизации — таски борятся довольно эффективно.


C>Стоит ли надеяться, что можно достигнуть большей производительности другим способом?

Разумеется. Делаете свой пул нативных потоков, поверх него изобретаете зелёные потоки или таски, добавляете load balancing + work stealing и в довершение переводите всю эту радость на lock-free, чтобы избежать лишних блокировок

Вполне возможно, есть чуть более быстрые или более удобные (для какой-то частной задачи) реализации того же самого, но общая идея не меняется.
Re[2]: Красивое решение для задачи с потоками
От: WolfHound  
Дата: 13.03.15 01:15
Оценка: 12 (2) +2
Здравствуйте, Pzz, Вы писали:

Pzz>Ну, на вскидку:

Рабочий поток будет вечно круги нарезать.
Рабочий поток может начать работать до того как получит задание.
итп

Можно обойтись одним барьером:
  Barrier barrier = new Barrier(3 + 1);//3 рабочих потока плюс 1 управляющий
...
  while (true)
  {
    раздать_задания();
    barrier.SignalAndWait();//запускает рабочие потоки
    barrier.SignalAndWait();//ждет, когда рабочие потоки закончат работу
  }
...
  while (true)
  {
    barrier.SignalAndWait();//ждёт задание
    сделать_работу();
    barrier.SignalAndWait();//запускает получение нового задания
  }
... << RSDN@Home 1.2.0 alpha 5 rev. 62>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re: Красивое решение для задачи с потоками
От: Sinix  
Дата: 13.03.15 09:21
Оценка: 64 (3)
Здравствуйте, Cynic, Вы писали:

C>Требуется найти "красивое" решение для следующей задачи.

C>Есть четыре потока: один "управляющий", и три "вычислительных". Задача "управляющего потока" раздавать задачи "вычислительным потокам", а задача "вычислительных" выполнять эти задачи. При этом "вычислительные потоки" должны работать по следующему алгоритму:

Таски наше всё. Пишется за 5 минут, работает с первого раза.
UPD: НЕ работает с первого раза, см ниже.
  код
    enum State
    {
        Idle, GetResult, Process
    }

    static volatile State state;

    static async Task<int> GetNextResult(int i)
    {
        Debug.Assert(state == State.GetResult, "Fail");
        Console.WriteLine("Geting result #{0}", i);

        var delayMs = (i % 5) * 200;
        await Task.Delay(TimeSpan.FromMilliseconds(delayMs));

        Debug.Assert(state == State.GetResult, "Fail");
        Console.WriteLine("Result #{0} ready, moving on", i);

        return i;
    }

    static async Task<int> Process(int worker, int i)
    {
        Debug.Assert(state == State.Process, "Fail");
        Console.WriteLine("Worker{0}: processing result #{1}", worker, i);

        var delayMs = (i % 7) * 150;
        await Task.Delay(TimeSpan.FromMilliseconds(delayMs));

        Debug.Assert(state == State.Process, "Fail");
        Console.WriteLine("Worker{0}: result #{1} processed", worker, i);
        return i;
    }

    static async Task Run(CancellationToken ct)
    {
        int num = 0;
        int taskCount = 3;
        while (!ct.IsCancellationRequested)
        {
            state = State.GetResult;
            var tasks = Enumerable.Range(0, taskCount).Select(i => GetNextResult(num + i));

            await Task.WhenAll(tasks);

            state = State.Process;
            tasks = Enumerable.Range(0, taskCount).Select(i => Process(i, num + i));

            await Task.WhenAll(tasks);

            state = State.Idle;
            num += taskCount;
        }
    }

    static void Main()
    {
        Console.WriteLine("Starting, any key to exit");
        var cts = new CancellationTokenSource();
        var task = Run(cts.Token);

        Console.ReadKey();

        cts.Cancel();
        task.Wait();
    }


Это на случай, если получение-обработку надо разделить. Если не надо, всё тоже несложно — producer-consumer и ConcurrentQueue.

UPD: Позорище и ещё раз позорище

Вот это
        while (!ct.IsCancellationRequested)
        {
            state = State.GetResult;
            var tasks = Enumerable.Range(0, taskCount).Select(i => GetNextResult(num + i)); // (1)

            await Task.WhenAll(tasks);

            state = State.Process;
            tasks = Enumerable.Range(0, taskCount).Select(i => Process(i, num + i)); // (2)

            await Task.WhenAll(tasks);

            state = State.Idle;
            num += taskCount;
        }

надо заменить на
        while (!ct.IsCancellationRequested)
        {
            state = State.GetResult;
            var tasks = Enumerable.Range(0, taskCount).Select(i => GetNextResult(num + i)).ToArray(); // (1)

            await Task.WhenAll(tasks);

            state = State.Process;
            var tasks2 = tasks.Select((t, i) => Process(i, t.Result)).ToArray(); // (2)

            await Task.WhenAll(tasks2);

            num += taskCount;
            state = State.Idle;
        }


С (2) всё понятно — надо обрабатывать результат, а не "Enumerable.Range(0, taskCount)".

С (1) всё чуть сложнее. Кто сообразит, почему там нужен .ToArray() (подчеркнул) — тому пряник.

UPD2:
Ну и правильный вариант использования "Task.WhenAll()", чтобы закрыть тему.
        while (!ct.IsCancellationRequested)
        {
            state = State.GetResult;
            var resultTasks = Enumerable.Range(0, taskCount).Select(i => GetNextResult(num + i));
            var results = await Task.WhenAll(resultTasks);

            state = State.Process;
            var processingTasks = results.Select((result, ix) => Process(ix, result)).ToArray();
            await Task.WhenAll(processingTasks);

            num += taskCount;
            state = State.Idle;
        }


Мораль: пишите ассерты.
Отредактировано 13.03.2015 11:20 Sinix . Предыдущая версия . Еще …
Отредактировано 13.03.2015 10:50 Sinix . Предыдущая версия .
Re[11]: Красивое решение для задачи с потоками
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 16.03.15 23:47
Оценка: 8 (1) +2
Здравствуйте, G_a_r_r_y, Вы писали:

AVK>>Там есть Task.WhenAll, красивости ничего принципиально не меняют.

G__>Одно из исходных требований: "Управляющий поток должен дожидаться пока все вычислительные потоки не встанут на паузу и только после этого раздавать им задачи". Поэтому в данном примере нельзя использовать Task.WhenAll

Можно. Потому что там именно логически нужно дождаться, для симуляции, а не физически поток CPU лочить.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re: Красивое решение для задачи с потоками
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 13.03.15 10:03
Оценка: +3
Здравствуйте, Cynic, Вы писали:

C>Поэтому решил спросить Вас, есть ли способ более красиво решить требуемую задачу?


1) Самый правильный способ — забить вообще про игры с потоками и просто использовать TPL. Потому что очень похоже что ты изобретаешь плохую и тормозную его версию.
2) Если уж хочется руками, то выкинуть примитивы ОС — они медленные, могут дать провал перформанса на 1-2 порядка. Вместо этого использовать lock, ReaderWriterLockSlim и Monitor.
3) Использовать параллельные коллекции типа ConcurrentQueue — большая часть операций с ними реализована lock free, это может еще до 10-20% прибытку в перформансе дать.
4) А дальше чисто алгоритмыческие фишки типа work stealing или локализации областей совместно изменяемых данных, но про них без подробного описания твоей задачи говорить бессмысленно.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[4]: Красивое решение для задачи с потоками
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 14.03.15 13:05
Оценка: 57 (2)
Здравствуйте, Sinix, Вы писали:

S>Из недавнего — только Concurrency in C# Cookbook попадалась. Так себе.


Так есть же православный аналог — https://www.packtpub.com/application-development/multithreading-c-50-cookbook
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[3]: Красивое решение для задачи с потоками
От: Sinix  
Дата: 13.03.15 06:59
Оценка: +1 -1
Здравствуйте, mapnik, Вы писали:

M>Отличный совет! А где же совет учить Windows Longhorn, ObjectSpaces и WinFS. И что там еще у MS инновационного?

С разморозкой

Таски были "инновационными" в 2007м, если чо.
Re: Красивое решение для задачи с потоками
От: IB Австрия http://rsdn.ru
Дата: 13.03.15 09:43
Оценка: +2
Здравствуйте, Cynic, Вы писали:


C>Поэтому решил спросить Вас, есть ли способ более красиво решить требуемую задачу?

Есть, можно сказать, уже классическая статья Joe Albahary Threading in C#. Там множество наглядных примеров, в том числе и на аналогичные задачи, если я правильно помню.
Мы уже победили, просто это еще не так заметно...
Красивое решение для задачи с потоками
От: Cynic Россия  
Дата: 12.03.15 21:06
Оценка: -1
Требуется найти "красивое" решение для следующей задачи.
Есть четыре потока: один "управляющий", и три "вычислительных". Задача "управляющего потока" раздавать задачи "вычислительным потокам", а задача "вычислительных" выполнять эти задачи. При этом "вычислительные потоки" должны работать по следующему алгоритму:
Кроме того:
Пытаясь решить эту задачу я написал следующий тестовый пример:
    class Program
    {
        static AutoResetEvent mt = new AutoResetEvent(false);
        static AutoResetEvent th1 = new AutoResetEvent(false);
        static AutoResetEvent th2 = new AutoResetEvent(false);
        static AutoResetEvent th3 = new AutoResetEvent(false);

        static Thread mainThread;
        static Thread thread1;
        static Thread thread2;
        static Thread thread3;

        static void Main(string[] args)
        {
            mainThread = new Thread(MainThread);
            mainThread.IsBackground = true;

            thread1 = new Thread(Thread1);
            thread1.IsBackground = true;

            thread2 = new Thread(Thread2);
            thread2.IsBackground = true;

            thread3 = new Thread(Thread3);
            thread3.IsBackground = true;

            thread1.Start();
            thread2.Start();
            thread3.Start();
            mainThread.Start();

            Console.ReadLine();
        }

        static void MainThread()
        {
            while (true)
            {
                Console.Write("\nT ");

                if ((thread1.ThreadState & ThreadState.WaitSleepJoin) != ThreadState.WaitSleepJoin ||
                    (thread2.ThreadState & ThreadState.WaitSleepJoin) != ThreadState.WaitSleepJoin ||
                    (thread3.ThreadState & ThreadState.WaitSleepJoin) != ThreadState.WaitSleepJoin)
                {
                    mt.WaitOne();
                }

                if ((thread1.ThreadState & ThreadState.Running) == ThreadState.Running ||
                    (thread2.ThreadState & ThreadState.Running) == ThreadState.Running ||
                    (thread3.ThreadState & ThreadState.Running) == ThreadState.Running)
                {
                    th1.Set();
                    th2.Set();
                    th3.Set();
                }

                Thread.Sleep(1000);
            }
        }

        static void Thread1()
        {
            while (true)
            {
                mt.Set();
                th1.WaitOne();
                Console.Write("1");
            }
        }

        static void Thread2()
        {
            while (true)
            {
                mt.Set();
                th2.WaitOne();
                Console.Write("2");
            }
        }

        static void Thread3()
        {
            while (true)
            {
                mt.Set();
                th3.WaitOne();
                Console.Write("3");
            }
        }
    }

И всё вроде работает, но есть две вещи которые меня не устраивают:
Не устраивают они меня потому, что пока нужно только три "вычислительных" потока с этим можно жить, а представьте во что это превратиться если их будет пару десятков.
Поэтому решил спросить Вас, есть ли способ более красиво решить требуемую задачу?
:)
Отредактировано 12.03.2015 21:52 Cynic . Предыдущая версия . Еще …
Отредактировано 12.03.2015 21:09 Cynic . Предыдущая версия .
Отредактировано 12.03.2015 21:08 Cynic . Предыдущая версия .
Отредактировано 12.03.2015 21:08 Cynic . Предыдущая версия .
Отредактировано 12.03.2015 21:07 Cynic . Предыдущая версия .
Re[3]: Красивое решение для задачи с потоками
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 14.03.15 13:01
Оценка: +1
Здравствуйте, Cynic, Вы писали:

C>По поводу TPL: Правильно ли я понимаю, что TPL реализует паттерн поставщик-потребитель, т.е. создаёт один или несколько рабочих потоков и скармливает им задачи.


Дефолтный шедулер внутри — примерно так, только очень грубо. Но это не должно тебя особо волновать. Тебе нужно разделить свою задачу на кусочки и продекларировать между ними зависимости. С остальным TPL разберется сам.

C> При это сами потоки не создаются и уничтожаются каждый раз для новой задачи?


Дефолтный шедулер берет потоки из стандартного пула. Не переживай за такие вещи — TPL в плане перформанса оттюнен очень качественно.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[5]: Красивое решение для задачи с потоками
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 14.03.15 13:01
Оценка: +1
Здравствуйте, Cynic, Вы писали:

C>В общем попробовал я таски и офигел. Ни каким другим из предложенных способов даже рядом не удавалось достигнуть той-же эффективности. Производительность других решений на 2-3 порядка ниже.


2-3 порядка, это потому что вместо спинлоков ты примитивы ОС используешь, а тела методов у тебя игрушечные. Кстати, вместо рукопашных циклом можно использовать Thread.SpinWait().

C>
  • Каким образом удалось достигнуть такой производительности?

    В данном случае спинлоки прежде всего.

    C>
  • Стоит ли надеяться, что можно достигнуть большей производительности другим способом?

    Вопрос непонятный.
    ... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
  • AVK Blog
    Re[5]: Красивое решение для задачи с потоками
    От: Sinix  
    Дата: 15.03.15 21:12
    Оценка: :)
    Здравствуйте, Sharov, Вы писали:

    S>Вот хороший блог -- http://blogs.msdn.com/b/pfxteam/

    Тынц по ссылке "вот этим
    Автор: Sinix
    Дата: 17.09.13
    " из предыдущего поста :P
    Re[9]: Красивое решение для задачи с потоками
    От: AndrewVK Россия http://blogs.rsdn.org/avk
    Дата: 16.03.15 11:50
    Оценка: +1
    Здравствуйте, G_a_r_r_y, Вы писали:

    G__>У меня пример для 4 фреймворка (посыпаю голову пеплом, забыл предупредить), там этих "красивостей" еще нет.


    Там есть Task.WhenAll, красивости ничего принципиально не меняют.
    ... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
    AVK Blog
    Re[4]: Красивое решение для задачи с потоками
    От: Cynic Россия  
    Дата: 18.03.15 21:29
    Оценка: -1
    Здравствуйте, Sinix, Вы писали:

    S>например

    S>
    S>        public async Task<IWorker> GetWorker()
    S>        {
    S>            Console.WriteLine("Worker {0} start", _name);
    
    S>            await Task.Delay(_delay);
    
    S>            Console.WriteLine("Worker {0} end", _name);
    S>            return worker;
    S>        }
    S>


    Тут я не подумал конечно, заменил на Thread.Sleep. Асинхронным метод делать не хочу, его задача возвращать таски. Мои таски не должны возвращать значение (void), т.к. предполагается что они изменяют поля класса в котором они созданы.

    S>Если метод асинхронный — он должен возвращать Task, чтобы было его дожидаться, например

    S>
    S>        public static Task<IWorker> Start()
    S>        {
    S>            return Worker(_cts.Token);
    S>        }
    S>


    Вы имеете ввиду, что по тому как я использовал это метод в Main он должен быть асинхронным? Но он же работает, что плохого в такой реализации?

    Кстати в вашем примере я заметил, что вы объявляете метод как
    static async Task Run(CancellationToken ct)

    и даже вызываете его в Main
    var task = Run(cts.Token);

    но при этом в самом методе Run нет ни одного return Так и должно быть?

    В общем вот так всё работает:
        interface IWorker
        {
            Task GetWorker();
        }
    
        class Worker: IWorker
        {
            readonly string _name;
            readonly int _delay;
    
            public Worker(string name, int delay)
            {
                _name = name;
                _delay = delay;
    
                TimeEngine.Add(this);
            }
    
            public Task GetWorker()
            {
                Task worker = Task.Factory.StartNew(() =>
                {
                    Console.WriteLine("Worker {0} start", _name);
    
                    Thread.Sleep(_delay);
    
                    Console.WriteLine("Worker {0} end", _name);
                });
    
                return worker;
            }
        }
    
        class TimeEngine
        {
            static List<IWorker> _workers = new List<IWorker>();
            static CancellationTokenSource _cts = new CancellationTokenSource();
    
            public static void Add(IWorker worker)
            {
                _workers.Add(worker);
            }
    
            private static async Task Worker(CancellationToken ct)
            {
                while (!ct.IsCancellationRequested)
                {
                    var tasks = Enumerable.Range(0, _workers.Count).Select(x => _workers[x].GetWorker());
                    await Task.WhenAll(tasks);
                    Console.WriteLine();
                }
            }
    
            public static void Start()
            {
                Worker(_cts.Token);
            }
    
            public static void Stop()
            {
                _cts.Cancel();
            }
        }
    
        class Program
        {
            readonly static int _taskCount = 10;
            readonly static Random _rnd = new Random();
    
            static void Main(string[] args)
            {
                for (int i = 0; i < _taskCount; i++)
                {
                    new Worker(i.ToString(), _rnd.Next(500, 1500));
                }
    
                Console.WriteLine("TimeEngine start (Press any key to Stop)");
                TimeEngine.Start();
    
                Console.ReadKey();
    
                TimeEngine.Stop();
                Console.WriteLine("TimeEngine Stop");
    
                Console.ReadKey();
            }
        }
    :)
    Отредактировано 18.03.2015 21:54 Cynic . Предыдущая версия . Еще …
    Отредактировано 18.03.2015 21:51 Cynic . Предыдущая версия .
    Отредактировано 18.03.2015 21:50 Cynic . Предыдущая версия .
    Re[5]: Красивое решение для задачи с потоками
    От: Sinix  
    Дата: 19.03.15 06:04
    Оценка: +1
    Здравствуйте, Cynic, Вы писали:


    C>Тут я не подумал конечно, заменил на Thread.Sleep. Асинхронным метод делать не хочу, его задача возвращать таски. Мои таски не должны возвращать значение (void), т.к. предполагается что они изменяют поля класса в котором они созданы.


    Обычно все методы, возвращающие Task именно что асинхронные и возвращают hot task (уже работающую задачу). Проверка аргументов и подготовка запуска делаются конечно синхронно, а вот остальная часть обычно выполняется в фоне.
    Thread.Sleep и прочие ожидающие блокировки плохо сказываются на масштабируемости тасков. Если иначе никак — можно оставить, но пометить как проблемное место. (Я обычно такие места размечаю как // TOFIX: comment )


    C>Вы имеете ввиду, что по тому как я использовал это метод в Main он должен быть асинхронным? Но он же работает, что плохого в такой реализации?

    То, что у вас нет способа дождаться завершения операции и узнать о её результате (exception — тоже результат). В итоге приложение будет или внезапно падать, или молча проглатывать исключения (зависит от версии фреймворка и <ThrowUnobservedTaskExceptions/> (детали).

    Навскидку:
    http://theburningmonk.com/2012/10/c-beware-of-async-void-in-your-code/
    https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
    http://haacked.com/archive/2014/11/11/async-void-methods/
    http://www.jaylee.org/post/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.aspx
    http://www.silverlightplayground.org/post/2013/02/19/The-“evil”-in-using-“async-void”.aspx

    Но это относится не только к 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));
                }
    Re[7]: Красивое решение для задачи с потоками
    От: Sinix  
    Дата: 19.03.15 16:37
    Оценка: +1
    Здравствуйте, Cynic, Вы писали:


    C> В общем вот так вот правильно:

    В конкретно этом случае достаточно return Task.Run(), async/await тут не нужен.
    Re[9]: Красивое решение для задачи с потоками
    От: Sinix  
    Дата: 20.03.15 05:46
    Оценка: +1
    Здравствуйте, Cynic, Вы писали:


    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(...).
    Re: Красивое решение для задачи с потоками
    От: Pzz Россия https://github.com/alexpevzner
    Дата: 12.03.15 21:32
    Оценка:
    Здравствуйте, Cynic, Вы писали:

    C>Требуется найти "красивое" решение для следующей задачи.


    Ну, на вскидку:
    1. Заводим 2 объекта синхронизации. Один — системы ReaderWriterLock, другой — системы AutoResetEvent
    2. Управлающий поток крутится в цикле:
      while (true)
      {
        rwlock.AcquireWriterLock();
        раздать_задания():
        rwlock.ReleaseWriterLock();
        event.WaitOne()
      }

    3. Вычислительные потоки крутятся в цикле:
      while (true)
      {
        rwlock.AcquireReaderLock();
        сделать_работу();
        event.Set();
        rwlock.ReleaseReaderLock()
      }
    Re: Красивое решение для задачи с потоками
    От: mapnik США http://www.hooli.xyz/
    Дата: 13.03.15 07:38
    Оценка:
    Здравствуйте, Cynic, Вы писали:

    C>Производительность решения очень важна!


    Я бы использовал Monitor вместо AutoResetEvent. AutoResetEvent довольно медленное решение. Напр. здесь есть некоторая статистика по методам синхронизации для потоков, возможно она будет вам полезна — http://blog.teamleadnet.com/2012/02/why-autoresetevent-is-slow-and-how-to.html
    Re[3]: Красивое решение для задачи с потоками
    От: Pzz Россия https://github.com/alexpevzner
    Дата: 13.03.15 07:59
    Оценка:
    Здравствуйте, WolfHound, Вы писали:

    Pzz>>Ну, на вскидку:

    WH>Рабочий поток будет вечно круги нарезать.

    Да, согласен.
    Re: Красивое решение для задачи с потоками
    От: Sharov Россия  
    Дата: 13.03.15 08:02
    Оценка:
    Здравствуйте, Cynic, Вы писали:

    C>Требуется найти "красивое" решение для следующей задачи.

    C>Есть четыре потока: один "управляющий", и три "вычислительных". Задача "управляющего потока" раздавать задачи "вычислительным потокам", а задача "вычислительных" выполнять эти задачи. При этом "вычислительные потоки" должны работать по следующему алгоритму:
    C> C>Кроме того:
    C>

    Если .net >= 4.0, то:
    1) Я бы посмотрел на блокирующую очередь для ожидания новой
    задачи;
    2) Для синхронизации ("дожидания") выполнения потоков барьер уже посоветовали выше.
    Кодом людям нужно помогать!
    Re: Красивое решение для задачи с потоками
    От: vorona  
    Дата: 13.03.15 09:31
    Оценка:
    Здравствуйте, Cynic, Вы писали:

    static class Programm
    {
        static void Main()
        {
            int count = 4;
            var workers = Enumerable.Range(1, count).Select(i => (Action)(() => Console.WriteLine(i))).ToArray();
    
            var tcs = new TaskCompletionSource<bool>();
            int index = 0;
            do
            {
                TaskCompletionSource<bool> tcs2 = tcs;
                foreach (Action worker in workers)
                {
                    Task.Run(async () =>
                    {
                        worker();
                        if ((Interlocked.Increment(ref index) % count) == 0)
                        {
                            tcs = new TaskCompletionSource<bool>();
                            tcs2.SetResult(true);
                        }
                        await tcs2.Task;
                    });
                }
                tcs2.Task.Wait();
                Console.WriteLine("do work, or esc for exit");
            }
            while (Console.ReadKey().Key != ConsoleKey.Escape);
        }
    }
    Re[2]: Красивое решение для задачи с потоками
    От: Cynic Россия  
    Дата: 13.03.15 17:25
    Оценка:
    Здравствуйте, AndrewVK, Вы писали:
    AVK>2) Если уж хочется руками, то выкинуть примитивы ОС — они медленные, могут дать провал перформанса на 1-2 порядка. Вместо этого использовать lock, ReaderWriterLockSlim и Monitor.

    Я конечно извиняюсь, скорее всего я туплю, но как можно использовать например ReaderWriterLockSlim для моей задачи
    Мне нужно, чтобы например три рабочих потока взяли себе по задаче выполнили их и при этом каждый из них дождался пока закончат остальные, потом все снова взяли себе по задаче и т.д. lock() (ну и Monitor) я ещё как то смог прикрутить к этой задаче, а вот про ReaderWriterLockSlim идей чего то нету. Можно примерчик

    p/s: Про TPL подумаю.
    :)
    Отредактировано 13.03.2015 17:26 Cynic . Предыдущая версия .
    Re[2]: Красивое решение для задачи с потоками
    От: Cynic Россия  
    Дата: 14.03.15 07:57
    Оценка:
    Здравствуйте, nikov, Вы писали:

    N>Советую забыть про потоки и учить таски и TPL.


    По поводу TPL: Правильно ли я понимаю, что TPL реализует паттерн поставщик-потребитель, т.е. создаёт один или несколько рабочих потоков и скармливает им задачи. При это сами потоки не создаются и уничтожаются каждый раз для новой задачи? Вопрос такой возник, потому что важна производительность решения.
    :)
    Re[3]: Красивое решение для задачи с потоками
    От: Sinix  
    Дата: 14.03.15 10:26
    Оценка:
    Здравствуйте, Cynic, Вы писали:

    C>По поводу TPL: Правильно ли я понимаю, что TPL реализует паттерн поставщик-потребитель, т.е. создаёт один или несколько рабочих потоков и скармливает им задачи. При этом сами потоки не создаются и уничтожаются каждый раз для новой задачи? Вопрос такой возник, потому что важна производительность решения.


    Не совсем так. Скорее, таски — это довольно толковая реализация пары work item + шедулер.

    Шадулер может быть любой, по умолчанию используется крайне эффективный пул задач. Плюс с тасками и await очень удобно писать код с non-blocking wait. Вот тут
    Автор: Sinix
    Дата: 13.02.14
    был пример, можете попробовать достичь того же без тасков.
    Re[3]: Красивое решение для задачи с потоками
    От: AndrewVK Россия http://blogs.rsdn.org/avk
    Дата: 14.03.15 12:18
    Оценка:
    Здравствуйте, Cynic, Вы писали:

    C>Я конечно извиняюсь, скорее всего я туплю, но как можно использовать например ReaderWriterLockSlim для моей задачи


    RWLock вместо полной блокировки нужен при обращении к обычным дотнетовским коллекциям — они поддерживают режим с параллельным чтением без блокировки.

    C>Мне нужно, чтобы например три рабочих потока взяли себе по задаче выполнили их и при этом каждый из них дождался пока закончат остальные, потом все снова взяли себе по задаче и т.д.


    Типичная для TPL задача. Зачем велосипед городить неясно.
    И, кстати, это не задача, а твое решение уже. Было бы неплохо исходную задачу описать, зачем тебе потоки понадобились и именно 3, и почему они должны ждать завершения. Координация то исходно, наверное, не по потокам, а по результатам вычислений, верно?

    C> lock() (ну и Monitor) я ещё как то смог прикрутить к этой задаче, а вот про ReaderWriterLockSlim идей чего то нету. Можно примерчик


    У тебя же в реальном коде какие то данные появятся, а не просто пустые потоки крутиться будут, верно?
    ... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
    AVK Blog
    Re[2]: Красивое решение для задачи с потоками
    От: breee breee  
    Дата: 14.03.15 12:26
    Оценка:
    Здравствуйте, nikov, Вы писали:

    N>Советую забыть про потоки и учить таски и TPL.


    А какую-нибудь толковую книгу (или серию статей) по ним посоветуете?
    Re[4]: Красивое решение для задачи с потоками
    От: Cynic Россия  
    Дата: 14.03.15 12:45
    Оценка:
    Здравствуйте, Sinix, Вы писали:

    S>Шадулер может быть любой, по умолчанию используется крайне эффективный пул задач. Плюс с тасками и await очень удобно писать код с non-blocking wait. Вот тут
    Автор: Sinix
    Дата: 13.02.14
    был пример, можете попробовать достичь того же без тасков.


    В общем попробовал я таски и офигел. Ни каким другим из предложенных способов даже рядом не удавалось достигнуть той-же эффективности. Производительность других решений на 2-3 порядка ниже. Тестировал примерно так:
        class Program
        {
            static readonly int counterValue = 1000000;
            static readonly int taskCount = 30000;
    
            static void Main(string[] args)
            {
                List<Action> actions = new List<Action>();
    
                for (int i = 0; i < taskCount; i++)
                {
                    actions.Add(new Action(Work));
                }
    
                Console.WriteLine("Task speed test");
    
                DateTime start = DateTime.Now;
    
                Parallel.Invoke(actions.ToArray());
    
                DateTime stop = DateTime.Now;
    
                Console.WriteLine("Time is: {0} secs", (stop - start).TotalSeconds);
                Console.ReadLine();
            }
    
            public static void Work()
            {
                int counter = counterValue;
    
                while (true)
                {
                    counter--;
                    if (counter <= 0)
                        return;
                }
            }
        }

    Появилось несколько вопросов:
    1. Каким образом удалось достигнуть такой производительности?
    2. Стоит ли надеяться, что можно достигнуть большей производительности другим способом?
    3. Интересно что если менять параметры counterValue и taskCount увеличивая в 10 раз один и в 10 раз уменьшая другой (или наоборот), то время выполнения задач практически не меняется. На моей машине всегда примерно 9 секунд. Складывается впечатление, что создается ограниченное количество потоков которым потом по очереди скармливаются задачи. В общем как это работает?
    :)
    Отредактировано 14.03.2015 12:50 Cynic . Предыдущая версия .
    Re[3]: Красивое решение для задачи с потоками
    От: Sinix  
    Дата: 14.03.15 12:50
    Оценка:
    Здравствуйте, breee breee, Вы писали:

    N>>Советую забыть про потоки и учить таски и TPL.

    BB>А какую-нибудь толковую книгу (или серию статей) по ним посоветуете?

    Присоединяюсь к вопросу.

    Материалов мало, полтора года назад всё ограничивалось вот этим
    Автор: Sinix
    Дата: 17.09.13
    . Из недавнего — только Concurrency in C# Cookbook попадалась. Так себе.
    Re[4]: Красивое решение для задачи с потоками
    От: Cynic Россия  
    Дата: 14.03.15 12:52
    Оценка:
    Здравствуйте, AndrewVK, Вы писали:

    Как бы ноги растут отсюда — http://rsdn.ru/forum/design/5979819.1
    Автор: Cynic
    Дата: 12.03.15
    :)
    Отредактировано 20.03.2015 8:23 Cynic . Предыдущая версия .
    Re[6]: Красивое решение для задачи с потоками
    От: Cynic Россия  
    Дата: 15.03.15 09:16
    Оценка:
    Здравствуйте, AndrewVK, Вы писали:

    AVK> Кстати, вместо рукопашных циклом можно использовать Thread.SpinWait().


    Не понимаю где мене тут это может помочь.

    C>>
  • Стоит ли надеяться, что можно достигнуть большей производительности другим способом?

    AVK>Вопрос непонятный.


    Можно ли добиться большей производительности другим способом? Или выигрыш будет не значительным и не стоит заморачиваться?
  • :)
    Re[7]: Красивое решение для задачи с потоками
    От: AndrewVK Россия http://blogs.rsdn.org/avk
    Дата: 15.03.15 10:32
    Оценка:
    Здравствуйте, Cynic, Вы писали:

    AVK>> Кстати, вместо рукопашных циклом можно использовать Thread.SpinWait().

    C>Не понимаю где мене тут это может помочь.

    int counter = counterValue;
    while (true)
    {
            counter--;
            if (counter <= 0)
                    return;
    }


    Вместо этого можно просто Thread.SpinWait(counterValue).

    C>>>
  • Стоит ли надеяться, что можно достигнуть большей производительности другим способом?
    AVK>>Вопрос непонятный.
    C>Можно ли добиться большей производительности другим способом? Или выигрыш будет не значительным и не стоит заморачиваться?

    Не стоит заморачиваться.
    ... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
  • AVK Blog
    Re[4]: Красивое решение для задачи с потоками
    От: Sharov Россия  
    Дата: 15.03.15 17:53
    Оценка:
    Здравствуйте, Sinix, Вы писали:

    S>Присоединяюсь к вопросу.


    S>Материалов мало, полтора года назад всё ограничивалось вот этим
    Автор: Sinix
    Дата: 17.09.13
    . Из недавнего — только Concurrency in C# Cookbook попадалась. Так себе.


    Вот хороший блог -- http://blogs.msdn.com/b/pfxteam/
    Кодом людям нужно помогать!
    Re[6]: Красивое решение для задачи с потоками
    От: G_a_r_r_y  
    Дата: 15.03.15 23:23
    Оценка:
    Здравствуйте

    Поразмял извилины перед понедельником
    Ничего особенного, но может кому-то будет интересно.

      [TestClass]
      public class ParallelRunUnitTest {
        public TestContext TestContext { get; set; }
    
        const int _actionsCount = 100;
        const int _threadsCount = 3;
        int _doneCount;
        readonly Queue<Action> _actions = new Queue<Action>(_actionsCount);
        bool _active;
    
        [TestMethod]
        public void UsingThreadsAndMonitorTest() {
          _doneCount = 0;
          _active = true;
          var threadList = new List<Thread>(_threadsCount);
          for (int i = 0; i < _threadsCount; i++) {
            var thread = new Thread(ThreadMethod) { IsBackground = true };
            threadList.Add(thread);
            thread.Start();
          }
    
          var sw = Stopwatch.StartNew();
    
          lock (_actions)
            for (int i = _actionsCount; i > 0;) {
              Monitor.Wait(_actions, 5);
              if (_actionsCount - i == _doneCount) {
                for (int k = Math.Min(_threadsCount, i); k > 0; k--, i--)
                  _actions.Enqueue(TaskMethod);
                //Debug.WriteLine("    Add new tasks. Count: " + _actions.Count);
              }
              Monitor.Pulse(_actions);
            }
    
          // Подождать окончания расчета
          lock (_actions)
            while (_doneCount < _actionsCount)
              Monitor.Wait(_actions, 5);
    
          sw.Stop();
    
          // Подождать остановки потоков
          _active = false;
          foreach (var thread in threadList)
            thread.Join();
    
          Debug.WriteLine("  RunCount: {0}   Elapsed: {1}", _doneCount, sw.Elapsed);
        }
    
        void ThreadMethod() {
          while (_active) {
            Action action;
            lock (_actions) {
              while (_actions.Count == 0)
                if (_active)
                  Monitor.Wait(_actions, 200);
                else
                  return;
              action = _actions.Dequeue();
              Monitor.Pulse(_actions);
            }
            action();
          }
        }
    
        void TaskMethod() {
          const int count = 500000;
          for (int j = 0; j < 2; j++) 
          for (int i = 0; i < count; i++) {
            var a = Math.Sin(2*Math.PI*i/count);
          }
          Interlocked.Increment(ref _doneCount);
          //Debug.WriteLine("    Task done");
        }
    
        [TestMethod]
        public void UsingTasksTest() {
          _doneCount = 0;
          var tasks = new List<Task>(_threadsCount);
          var sw = Stopwatch.StartNew();
    
          for (int i = _actionsCount; i > 0;) {
            for (int k = Math.Min(_threadsCount, i); k > 0; k--, i--) {
              var task = new Task(TaskMethod);
              tasks.Add( task );
              task.Start();
            }
            //Debug.WriteLine("    Add new tasks. Count: " + tasks.Count);
            Task.WaitAll(tasks.ToArray());
            tasks.Clear();
          }
    
          sw.Stop();
          Debug.WriteLine("  RunCount: {0}   Elapsed: {1}", _doneCount, sw.Elapsed);
        }
      }


    Время выполнения тестов:
    UsingThreadsAndMonitorTest() : 3,23 секунды
    UsingTasksTest() : 3,17 секунды
    Re[7]: Красивое решение для задачи с потоками
    От: Sinix  
    Дата: 16.03.15 06:11
    Оценка:
    Здравствуйте, G_a_r_r_y, Вы писали:

    G__>Ничего особенного, но может кому-то будет интересно.


    Не нравится мне эта синхронизация из костылей и спичек (аля "while (_active)" и "_actionsCount — i == _doneCount"). Одна опечатка и упс. Собственно, она у вас уже есть, поля надо бы volatile пометить

    Вариант с тасками гораздо проще. Только Task.WaitAll() лучше заменить на await, ждущая блокировка тут не нужна. И "new Task() ... Start()" — на "Task.Run()";
    Более-менее правильный пример выше
    Автор: Sinix
    Дата: 13.03.15
    .
    Re[8]: Красивое решение для задачи с потоками
    От: G_a_r_r_y  
    Дата: 16.03.15 09:21
    Оценка:
    Здравствуйте, Sinix, Вы писали:

    S>Не нравится мне эта синхронизация из костылей и спичек (аля "while (_active)" и "_actionsCount — i == _doneCount"). Одна опечатка и упс.

    Никаких костылей и спичек, хотя выглядит сложновато, но можно и упростить, сразу не сообразил.

    S>Вариант с тасками гораздо проще. Только Task.WaitAll() лучше заменить на await, ждущая блокировка тут не нужна. И "new Task() ... Start()" — на "Task.Run()";

    У меня пример для 4 фреймворка (посыпаю голову пеплом, забыл предупредить), там этих "красивостей" еще нет.
    А ждущая блокировка нужна по условиям задачи.

    S>Более-менее правильный пример выше
    Автор: Sinix
    Дата: 13.03.15
    .

    Мой пример лучше.
    Отредактировано 16.03.2015 9:22 G_a_r_r_y . Предыдущая версия .
    Re[10]: Красивое решение для задачи с потоками
    От: G_a_r_r_y  
    Дата: 16.03.15 20:19
    Оценка:
    Здравствуйте, AndrewVK, Вы писали:

    AVK>Там есть Task.WhenAll, красивости ничего принципиально не меняют.

    Одно из исходных требований: "Управляющий поток должен дожидаться пока все вычислительные потоки не встанут на паузу и только после этого раздавать им задачи". Поэтому в данном примере нельзя использовать Task.WhenAll, а нужна именно блокировка Task.WaitAll управляющего потока.
    Re[2]: Красивое решение для задачи с потоками
    От: Cynic Россия  
    Дата: 18.03.15 15:55
    Оценка:
    Здравствуйте, Sinix, Вы писали:
    S> ...

    Попробовал сделать как у Вас:
        interface IWorker
        {
            Task GetWorker();
        }
    
        class Worker: IWorker
        {
            readonly string _name;
            readonly int _delay;
    
            public Worker(string name, int delay)
            {
                _name = name;
                _delay = delay;
    
                TimeEngine.Add(this);
            }
    
            public Task GetWorker()
            {
                Task worker = Task.Factory.StartNew(() =>
                {
                    Console.WriteLine("Worker {0} start", _name);
    
                    Task.Delay(_delay);
    
                    Console.WriteLine("Worker {0} end", _name);
                });
    
                return worker;
            }
        }
    
        class TimeEngine
        {
            static List<IWorker> _workers = new List<IWorker>();
            static CancellationTokenSource _cts = new CancellationTokenSource();
    
            public static void Add(IWorker worker)
            {
                _workers.Add(worker);
            }
    
            private static Task Print(int x)
            {
                Console.WriteLine(x);
                return new Task(() => { });
            }
    
            private static async Task Worker(CancellationToken ct)
            {
                while (!ct.IsCancellationRequested)
                {
                    var tasks = Enumerable.Range(0, _workers.Count).Select(x => _workers[x].GetWorker());
                    await Task.WhenAll(tasks);
                    await Task.Delay(TimeSpan.FromSeconds(1));
                }
            }
    
            public static void Start()
            {
                Worker(_cts.Token);
            }
    
            public static void Stop()
            {
                _cts.Cancel();
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Worker worker1 = new Worker("1", 1000);
                Worker worker2 = new Worker("2", 500);
                Worker worker3 = new Worker("3", 1200);
    
                Console.WriteLine("TimeEngine start (Press any key to Stop)");
                TimeEngine.Start();
    
                Console.ReadLine();
    
                TimeEngine.Stop();
                Console.WriteLine("Time Engine Stop");
    
                Console.ReadLine();
            }
        }

    Посмотрите плз. ни где не накосячил
    :)
    Re[3]: Красивое решение для задачи с потоками
    От: Sinix  
    Дата: 18.03.15 16:36
    Оценка:
    Здравствуйте, Cynic, Вы писали:

    C>Попробовал сделать как у Вас:


    Не, это совсем не как у меня
    Я завтра с утра напишу подробней, коротко: методы Task.Delay(), Task.Start() etc никак не влияют на текущий поток, они запускают новую задачу. Чтобы получить результат задачи — или task.Wait() / task.Result (блокирующее ожидание), или await.

    например
            public async Task<IWorker> GetWorker()
            {
                Console.WriteLine("Worker {0} start", _name);
    
                await Task.Delay(_delay);
    
                Console.WriteLine("Worker {0} end", _name);
                return worker;
            }


    Если метод асинхронный — он должен возвращать Task, чтобы было его дожидаться, например
            public static Task<IWorker> Start()
            {
                return Worker(_cts.Token);
            }


    Я в каком-то из ответов приводил более-менее рабочий пример, начните с него. Если что непонятно — спрашивайте.

    Сейчас сорри, занят, подробнее отвесу завтра.
    Re[6]: Красивое решение для задачи с потоками
    От: Cynic Россия  
    Дата: 19.03.15 10:00
    Оценка:
    Здравствуйте, Sinix, Вы писали:

    В общем спасибо, почитаю я ещё
    :)
    Re[6]: Красивое решение для задачи с потоками
    От: Cynic Россия  
    Дата: 19.03.15 16:01
    Оценка:
    Здравствуйте, 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);
                });
            }
    :)
    Отредактировано 19.03.2015 16:14 Cynic . Предыдущая версия .
    Re[8]: Красивое решение для задачи с потоками
    От: Cynic Россия  
    Дата: 19.03.15 21:42
    Оценка:
    Здравствуйте, Sinix, Вы писали:

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



    C>> В общем вот так вот правильно:

    S>В конкретно этом случае достаточно return Task.Run(), async/await тут не нужен.

    Да почему блин
    :)
    Отредактировано 19.03.2015 21:43 Cynic . Предыдущая версия .
    Re[10]: Красивое решение для задачи с потоками
    От: Cynic Россия  
    Дата: 20.03.15 08:41
    Оценка:
    Здравствуйте, Sinix, Вы писали:

    S>Получается, код эквивалентен return Task.Run(...).


    Вы правы, наверное я действительно что-то путаю. В общем решил вот так продолжить — http://rsdn.ru/forum/dotnet/5988673.1
    Автор: Cynic
    Дата: 20.03.15

    Спасибо
    :)
     
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.