В чем разница между задачей и потоком?

378

В C # 4.0 мы имеем Taskв пространстве имен System.Threading.Tasks . В чем истинная разница между Threadи Task. Я сделал несколько примеров программ (помощь взята из MSDN) для собственного обучения

Parallel.Invoke 
Parallel.For 
Parallel.ForEach 

но есть много сомнений, так как идея не так ясна.

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

hippietrail
источник
8
темы запускают задачи
pm100

Ответы:

315

Задача - это то, что вы хотите сделать.

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

В терминах .NET 4.0 Задача представляет собой асинхронную операцию. Поток (ы) используются для завершения этой операции, разбивая работу на куски и назначая отдельным потокам.

Митч Пшеничный
источник
Не могли бы вы представить элементарный пример работы потоков для выполнения задачи? Я не знаю, выполняют ли потоки работу, которая не зависит друг от друга, или они делают какой-то расчет командной работы ?
Pensum
Возможны оба сценария: в оптимальной ситуации потоки выполняют независимую работу без необходимости синхронизации с другими потоками. На практике блокировки используются для координации потоков.
Митч Уит
451

С точки зрения информатики, Taskэто будущее или обещание . (Некоторые люди используют эти два термина синонимно, некоторые используют их по-разному, никто не может договориться о точном определении.) В основном, Task<T>«обещания» вернуть вам T, но не сейчас, дорогая, я вроде занят, почему бы и нет ты вернешься позже?

Это Threadспособ выполнить это обещание. Но не каждому Taskнужен новенький Thread. (На самом деле создание потока часто нежелательно, потому что это намного дороже, чем повторное использование существующего потока из пула потоков. Об этом чуть позже.) Если ожидаемое вами значение приходит из файловой системы или из базы данных или сети, то нет необходимости для потока сидеть и ждать данных, когда он может обслуживать другие запросы. Вместо этого он Taskможет зарегистрировать обратный вызов для получения значений, когда они будут готовы.

В частности, Taskэто не сказать , почему это, что это занимает так много времени , чтобы вернуть значение. Это может быть , что это занимает много времени , чтобы вычислить, или это может быть , что это занимает много времени для извлечения. Только в первом случае вы могли бы использовать Threadдля запуска Task. (В .NET потоки очень дорогие, поэтому вы, как правило, хотите избегать их настолько, насколько это возможно, и действительно используете их только в том случае, если вы хотите выполнить несколько тяжелых вычислений на нескольких процессорах. Например, в Windows поток весит 12 КиБайт ( Я думаю), в Linux поток весит всего 4 КиБайта, в Erlang / BEAM даже всего 400 Байт. В .NET это 1 МиБайт!)

Йорг Миттаг
источник
29
Интересно, что в ранних предварительных выпусках TPL (Task Parallel Library) были Task and Future <T>. Будущее <T> было затем переименовано в Task <T>. :)
Ли Кэмпбелл
23
Как вы рассчитали 1 МБ для .NET?
dvallejo
5
@DanVallejo: Это число было упомянуто в интервью с командой разработчиков TPL. Я не могу сказать вам, кто это сказал или какое это было интервью, которое я наблюдал много лет назад.
Йорг Миттаг
9
@RIPUNJAYTRIPATHI Конечно, но это не должен быть другой поток, это может быть поток, который запросил работу в первую очередь.
Крис Питман
7
.NET просто использует потоки Windows в Windows, поэтому их размер одинаков - по умолчанию обычно 1 МБ виртуальной памяти для обоих. Физическая память используется по мере необходимости в кусках размером с страницу (обычно 64 КБ), то же самое с нативным кодом. Минимальный размер стека потоков зависит от ОС - например, 256 КБ для Vista. В x86 Linux по умолчанию обычно 2 МБ - опять же, выделяется кусками размером с страницу. (упрощение) Erlang использует только один системный поток на процесс, эти 400 байтов относятся к чему-то похожему на .NET Task.
Луаан
39

Нить

Простая вещь, вам, вероятно, не нужно ее использовать, вы, вероятно, можете использовать LongRunningзадачу и воспользоваться преимуществами TPL - Task Parallel Library, включенной в .NET Framework 4 (февраль 2002) и выше (также .NET ядро).

Задания

Абстракция над потоками. Он использует пул потоков (если вы не укажете задачу как LongRunningоперацию, если это так, для вас будет создан новый поток).

Пул потоков

Как следует из названия: пул потоков. .NET Framework обрабатывает ограниченное количество потоков для вас. Почему? Потому что открытие 100 потоков для выполнения дорогостоящих операций ЦП на процессоре с 8 ядрами определенно не очень хорошая идея. Фреймворк будет поддерживать этот пул для вас, повторно используя потоки (не создавая / не убивая их при каждой операции), и выполняя некоторые из них параллельно, таким образом, чтобы ваш ЦП не работал.

Хорошо, но когда использовать каждый?

В резюме: всегда используйте задачи.

Задача - это абстракция, поэтому ее намного проще использовать. Я советую вам всегда пытаться использовать задачи, и если вы сталкиваетесь с какой-то проблемой, которая заставляет вас обрабатывать поток самостоятельно (вероятно, в 1% случаев), то используйте потоки.

НО знать, что:

  • Привязка ввода / вывода : для связанных операций ввода / вывода (вызовов базы данных, файлов чтения / записи, вызовов API и т. Д.) Избегайте использования обычных задач, используйте LongRunningзадачи ( или потоки, если вам это нужно ). Потому что использование задач приведет вас к пулу потоков с несколькими занятыми потоками и множеством других задач, ожидающих своей очереди, чтобы занять пул.
  • CPU Bound : Для операций с CPU просто используйте обычные задачи (которые внутренне будут использовать пул потоков) и будьте счастливы.
fabriciorissetto
источник
Небольшая коррекция, Нить не "голая вещь". он реализован ОС, большинство реализаций используют функции CPU и CS, но они не реализованы аппаратно.
Томер W
7

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

Используйте Taskс TaskFactory.StartNew(Action action). Здесь вы выполняете делегат, поэтому, если вы не используете какой-либо поток, он будет выполнен в том же потоке (поток GUI). Если вы упоминаете поток, вы можете выполнить это Taskв другом потоке. Это ненужная работа, поскольку вы можете напрямую выполнить делегат или присоединить этот делегат к потоку и выполнить этот делегат в этом потоке. Так что не используйте это. это просто ненужно. Если вы намереваетесь оптимизировать свое программное обеспечение, это хороший кандидат на удаление.

** Обратите внимание, что Actionэто delegate.

Gryphes
источник
6

В дополнение к вышеуказанным пунктам было бы полезно знать, что:

  1. Задача по умолчанию является фоновой задачей. Вы не можете иметь задачу переднего плана. С другой стороны, поток может быть фоновым или передним (используйте свойство IsBackground, чтобы изменить поведение).
  2. Задачи, созданные в пуле потоков, перерабатывают потоки, что помогает экономить ресурсы. Поэтому в большинстве случаев задачи должны быть вашим выбором по умолчанию.
  3. Если операции выполняются быстро, гораздо лучше использовать задачу вместо потока. Для длительных операций задачи не дают больших преимуществ перед потоками.
user2492339
источник
4

Я обычно использую, Taskчтобы взаимодействовать с Winforms и простым фоновым рабочим, чтобы он не зависал. вот пример, когда я предпочитаю использоватьTask

private async void buttonDownload_Click(object sender, EventArgs e)
{
    buttonDownload.Enabled = false;
    await Task.Run(() => {
        using (var client = new WebClient())
        {
            client.DownloadFile("http://example.com/file.mpeg", "file.mpeg");
        }
    })
    buttonDownload.Enabled = true;
}

В.С.

private void buttonDownload_Click(object sender, EventArgs e)
{
    buttonDownload.Enabled = false;
    Thread t = new Thread(() =>
    {
        using (var client = new WebClient())
        {
            client.DownloadFile("http://example.com/file.mpeg", "file.mpeg");
        }
        this.Invoke((MethodInvoker)delegate()
        {
            buttonDownload.Enabled = true;
        });
    });
    t.IsBackground = true;
    t.Start();
}

разница в том, что вам не нужно использовать MethodInvokerболее короткий код.

ewwink
источник
4

Задача похожа на операцию, которую вы хотите выполнить, Thread помогает управлять этой операцией через несколько узлов процесса. Задача - это легкий вариант, так как многопоточность может привести к сложному управлению кодом.
Я предложу всегда читать из MSDN (Лучший в мире).

Задача

Нить

Саурабх
источник
3

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

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

Вы можете сделать то же самое с потоком (с большими усилиями), как вы можете с задачей.

Нить

int result = 0;
Thread thread = new System.Threading.Thread(() => { 
    result = 1; 
});
thread.Start();
thread.Join();
Console.WriteLine(result); //is 1

задача

int result = await Task.Run(() => {
    return 1; 
});
Console.WriteLine(result); //is 1

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

Как указывается в этой статье , задача предоставляет следующие мощные функции над потоком.

  • Задачи настроены на использование многоядерных процессоров.

  • Если у системы несколько задач, то она использует внутренний пул потоков CLR, и поэтому не возникает накладных расходов, связанных с созданием выделенного потока с использованием потока. Также уменьшите время переключения контекста между несколькими потоками.

  • Задача может вернуть результат. Не существует прямого механизма для возврата результата из потока.
  • Ожидание множества задач без сигнальной конструкции.

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

  • Установите отношения родитель / потомок, когда одна задача запускается из другой задачи.

  • Исключение дочерней задачи может распространиться на родительскую задачу.

  • Задача поддержки отмены посредством использования токенов отмены.

  • Асинхронная реализация проста в использовании, используя ключевые слова async и await.

CharithJ
источник