0

Я пытался определить лучший способ запуска кода одновременно/параллельно с остальной частью моего кода, возможно, используя поток. Из того, что я прочитал, с использованием Thread типа нет-нет в современном C#. Первоначально я думал Parallel.Invoke(), но это оказывается блокирующим вызовом до завершения всей внутренней работы.Task.Factory.StartNew не работает, как я ожидал

В моем приложении мне не нужно ждать завершения чего-либо, я не хочу получать результат, мне нужен код, который полностью не зависит от текущего потока. В принципе идея «огонь и забыть».

Из того, что, как я думал, я понимаю, Task.Factory.StartNew() - это правильный способ запуска части кода одновременно/параллельно с текущим кодом.

Основываясь на этом, я думал, что следующий код будет случайным образом распечатывать «ABABBABABAA».

void Main() 
{ 
    Task.Factory.StartNew(() => 
    { 
     for (int i = 0; i < 10; i++) 
     { 
      Console.Write("A"); 
     } 
    }); 

    for (int i = 0; i < 10; i++) 
    { 
     Console.Write("B"); 
    } 
} 

Однако, это:

  1. печать из «BBBBBBBBBBAAAAAAAAAA»
  2. Если я поменять Task.Factory.StartNew с for и наоборот та же последовательность печатается, что кажется странно.

Так что это заставляет меня думать, что Task.Factory.StartNew() фактически никогда не планировать работу к другому потоку, как будто вызывая StartNew является блокирующим вызовом.

Учитывая, что мне не нужны какие-либо результаты или ждать/await, было бы проще для меня просто создать новый Thread и запустить там мой код? Единственная проблема, что я с этим в том, что с помощью Thread кажется противоположность современной передовой практики и книга «Параллелизм в C# 6.0» говорится:

Как только вы наберете новую тему(), это над; Ваш проект уже имеет устаревший код.

+0

Нет ничего странного в выходе. Задача «будет запущена в какой-то момент после», это запланировано. На этот раз происходит после того, как цикл inline запущен (поскольку он запускался сразу после запланированной задачи), а тестовый код ничего интересного не показывает. Добавление в задержка 100 мс между каждой итерацией цикла в обоих случаях должно изменить наблюдение. – user2864740

+0

Хм, я вижу. Я изменил петли, чтобы иметь значительно более высокую ценность, теперь я вижу AAAAABBBBBAAAAA много. Так было ли это так, что я просто не уделял достаточно времени, чтобы задача была запланирована? – user9993

+1

В этом случае - да. Кроме того, большинство потоков фактически выполняются с чередованием, а не по-настоящему (это тривиально визуализировать в 1-ядерной системе, хотя n-ядерный процессор все равно будет запускать m потоков, где m >>> n). – user2864740

ответ

1

В моем приложении, мне не нужно ждать, пока что-нибудь, чтобы закончить, я не забочусь о получении результата, мне нужно код, который является полностью независимым от текущая нить. В принципе идея «огонь и забыть».

Уверены ли вы? «Огонь и забыть» на самом деле означает «Я отлично справляюсь с игнорированием исключений». Поскольку это не то, что требуется большинству людей, один общий подход заключается в том, чтобы поставить в очередь работу и сохранить Task, представляя эту работу, и присоединиться к ней в конце действия вашей программы, просто чтобы убедиться, что не-огонь Работа-и-забыть успешно завершается.

Я пытался определить лучший способ запуска кода одновременно/параллельно с остальной частью моего кода, возможно, используя поток. Из того, что я читал, использование типа Thread - это не-no в современном C#. Первоначально я думал Параллельный.Invoke(), но это оказывается блокирующим вызовом до завершения всей внутренней работы.

Действительно, параллельный код блокирует вызывающий поток. Этого можно избежать, обернув параллельный вызов в Task.Run (проиллюстрирован в Рецептуре 7.4 в моей книге).

В вашем конкретном случае (т. Е. В сценарии «огонь и забвение») вы можете просто отказаться от await и получить голый Task.Run. Хотя, как я упоминал выше, вероятно, лучше просто спрятать Task где-нибудь и await позже.

Из того, что, как я думал, я понимаю, Task.Factory.StartNew() - это правильный способ запуска части кода одновременно/параллельно с текущим запущенным кодом.

No, StartNew is dangerous и его следует использовать только в качестве крайней меры. Правильная техника - Task.Run, и если у вас есть действительно параллельный работу (то есть много кусков кода, привязанного к процессору), то обертка Task.Run вокруг Parallel/PLINQ будет лучше.

Основываясь на этом, я думал, что следующий код будет случайным образом распечатывать «ABABBABABAA».

Как others have noted, это только состояние гонки. Требуется время для работы очереди в пуле потоков, а компьютеры могут быстро подсчитываться до 10. (Запись вывода намного медленнее, но здесь все еще слишком быстро). Такая же проблема возникла бы с Task.Run (или руководством Thread).

4

На самом деле Task.Factory.StartNew работает только в отдельной ветке, единственная причина, по которой он проигрывает гонку каждый раз, - это время создания задачи. Вот код, чтобы доказать это

static void Main(string[] args) 
    { 
     Task.Factory.StartNew(() => 
     { 
      for (int i = 0; i < 10; i++) 
      { 
       Console.Write("A"); 
       Thread.Sleep(1); 
      } 
     }); 

     for (int i = 0; i < 10; i++) 
     { 
      Console.Write("B"); 
      Thread.Sleep(1); 
     } 
     Console.ReadKey(); 
    }