Есть ли случаи, когда предпочтительнее использовать простой старый объект Thread вместо одной из более новых конструкций?

112

Я вижу много людей в сообщениях в блогах и здесь на SO, которые либо избегают, либо советуют не использовать этот Threadкласс в последних версиях C # (и я имею в виду, конечно, 4.0+, с добавлением Task& друзей). Еще раньше велись споры о том, что функциональность простого старого потока во многих случаях может быть заменена ThreadPoolклассом.

Кроме того, другие специализированные механизмы делают этот Threadкласс менее привлекательным, например, Timers, заменяя уродливую комбинацию Thread+ Sleep, в то время как для графических интерфейсов у нас есть BackgroundWorkerи т. Д.

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

Итак, мой вопрос: есть ли случаи, когда необходимо или полезно использовать простой старый Threadобъект вместо одной из вышеуказанных конструкций?

тюдоровский
источник
4
Не придираться (или, может быть ... :)), но это .NET (т.е. не ограничивается C #).
MetalMikester
11
Пока что средняя репутация пользователя, ответившего на этот вопрос, составляет 64.5k. Довольно впечатляющий результат
zzzzBov
1
@zzzzBov: Я думал опубликовать свой собственный ответ, но теперь я не могу из-за страха понизить средний результат :)
Брайан Гидеон,
@BrianGideon, я не вижу причин, по которым это должно помешать вам или кому-либо еще ответить на этот вопрос.
zzzzBov
@ Брайан Гидеон: Пожалуйста, отправьте сообщение. :)
Tudor

Ответы:

107

Класс Thread нельзя сделать устаревшим, потому что он, очевидно, является деталью реализации всех упомянутых вами шаблонов.

Но это не совсем ваш вопрос; ваш вопрос

Есть ли случаи, когда необходимо или полезно использовать простой старый объект Thread вместо одной из вышеуказанных конструкций?

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

Мой совет: если вы окажетесь в ситуации, когда существующие инструменты более высокой абстракции не соответствуют вашим потребностям, и вы хотите реализовать решение с использованием потоков, вы должны определить недостающую абстракцию, которая вам действительно нужна , а затем реализовать эту абстракцию. используя потоки , а затем используйте абстракцию .

Эрик Липперт
источник
33
Всем хороший совет. Но есть ли у вас пример, когда простой старый объект потока может быть предпочтительнее одной из более новых конструкций?
Роберт Харви,
Отладку нескольких потоков с проблемами синхронизации для загрузки некоторого кода иногда можно сделать намного проще, чем с использованием других конструкций «более высокого» уровня. И вам в любом случае нужны базовые знания о работе и использовании потоков.
CodingBarfield
Спасибо за ответ, Эрик. В условиях недавней бомбардировки новых функций многопоточности действительно легко забыть, что иногда старый поток все еще необходим. Что ж, я полагаю, что знание голого металла в любом случае полезно.
Tudor
15
@ Роберт Харви: Конечно. Например, если у вас есть классическая ситуация «производитель / потребитель», когда вы хотите, чтобы один поток был выделен для извлечения материалов из рабочей очереди, а другой - для помещения материалов в рабочую очередь. Оба цикла навсегда; они плохо соотносятся с задачами, которые вы выполняете какое-то время, а затем получаете результат. Вам не нужен пул потоков, потому что есть только два потока. И вы можете быть умным, используя блокировки и сигналы, чтобы гарантировать безопасность очереди, не блокируя другой поток на долгое время.
Эрик Липперт
@Eric: Разве TPL Dataflow уже не предлагает такую ​​абстракцию ?
Аллон Гуралнек 03
52

Потоки являются основным строительным блоком для определенных вещей (а именно параллелизма и асинхронности), и поэтому их не следует убирать. Однако для большинства людей и в большинстве случаев использования есть более подходящие вещи, о которых вы упомянули, например, пулы потоков (которые обеспечивают хороший способ параллельной обработки множества небольших заданий без перегрузки машины путем одновременного создания 2000 потоков), BackgroundWorker( который инкапсулирует полезные события для одной недолговечной работы).

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

Аналогичным образом, .NETне запрещает использование массивов, несмотря на List<T>то , что он лучше подходит для многих случаев, когда люди используют массивы. Просто потому, что вы все еще можете создавать вещи, которые не охватываются стандартной библиотекой.

детеныш
источник
6
Тот факт, что автоматическая коробка передач работает 99% времени, не означает, что механическая коробка передач не имеет ценности, даже если игнорировать предпочтения водителя.
Guvante
4
Не очень знаком с идиомами вождения, учитывая, что я велосипедист и пользуюсь общественным транспортом. Но механическая коробка передач - это не сердце автоматической - это не механическая рука, перемещающая палку. Насколько я понимаю, это не совсем абстракция над другим.
Joey
2
Вы можете заменить слово «поток» на «неуправляемый код» во втором абзаце, и оно все равно будет звучать правдоподобно,
Конрад Фрикс,
1
@Joey: О, я думал, вы имели в виду часть устаревания, ценность мелкозернистого контроля за счет удобства использования, хотя большинству это никогда не понадобится. Кстати, технически интересной частью механической коробки передач в любом случае является сцепление.
Guvante
3
Если вы , ребята , действительно хотят идти с метафорой передачи, большинство последовательных передач (например, передачей SMG БМВ) являются , по сути, механической коробкой передач с компьютером Сцепление и рычагом переключения передач. Это не планетарные зубчатые передачи, как обычная автоматика.
Адам Робинсон,
13

Taskи Threadразные абстракции. Если вы хотите смоделировать поток, Threadкласс по-прежнему является наиболее подходящим выбором. Например, если вам нужно взаимодействовать с текущим потоком, я не вижу для этого лучших типов.

Однако, как вы отметили, .NET добавил несколько выделенных абстракций, которые Threadво многих случаях предпочтительнее .

Брайан Расмуссен
источник
9

ThreadКласс не устарел, он по - прежнему полезно при особых обстоятельствах.

Там, где я работаю, мы написали «фоновый процессор» как часть системы управления контентом: службу Windows, которая отслеживает каталоги, адреса электронной почты и RSS-каналы, и каждый раз, когда появляется что-то новое, выполняет над ней задачу - обычно для импорта данные.

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

MiMo
источник
Я понятия не имею, является ли использование Thread здесь правильным решением, но +1 для фактического предоставления примера, в котором требуется переход к Thread.
Oddthinking
6

Новые опции делают менее частым прямое использование и управление (дорогостоящими) потоками.

люди, которые, столкнувшись с задачей, требующей некоторого параллельного выполнения, сразу переходят к использованию старого доброго класса Thread.

Это очень дорогой и относительно сложный способ делать что-то параллельно.

Обратите внимание, что расходы имеют наибольшее значение: вы не можете использовать полный поток для выполнения небольшой работы, это было бы контрпродуктивно. ThreadPool борется с затратами, класс Task - со сложностями (исключения, ожидание и отмена).

Хенк Холтерман
источник
5

Чтобы ответить на вопрос , есть ли случаи, когда необходимо или полезно использовать простой старый объект Thread », я бы сказал, что простой старый Thread полезен (но не необходим), когда у вас есть длительный процесс, который вы выиграли » t никогда не взаимодействуют с из другого потока.

Например, если вы пишете приложение, которое подписывается на получение сообщений из какой-то очереди сообщений, и ваше приложение будет делать больше, чем просто обрабатывать эти сообщения, было бы полезно использовать поток, потому что поток будет самодостаточный (то есть вы не ждете, когда это будет сделано), и он не краткосрочен. Использование класса ThreadPool больше предназначено для постановки в очередь нескольких недолговечных рабочих элементов и позволяет классу ThreadPool эффективно управлять обработкой каждого из них по мере появления нового потока. Задачи можно использовать там, где вы бы использовали поток напрямую, но в приведенном выше сценарии я не думаю, что они вам много купят. Они помогают вам легче взаимодействовать с потоком (чего не делает в приведенном выше сценарии)

Дерек Грир
источник
Я согласен с тем, что большинство замечательных новых вещей, связанных с параллелизмом, разработано для краткосрочных мелких мелкозернистых задач, а длительные потоки по-прежнему должны быть реализованы с помощью System.IO.Threading.Thread.
Ben Voigt
4

Вероятно, это не тот ответ, которого вы ожидали, но я все время использую Thread при кодировании для .NET Micro Framework. MF довольно урезан и не включает абстракции более высокого уровня, а класс Thread очень гибкий, когда вам нужно получить последний бит производительности от процессора с низкой МГц.

Злой Хакер
источник
Эй, это действительно хороший пример! Спасибо. Я не думал о фреймворках, которые не поддерживают новые функции.
Tudor
3

Вы можете сравнить класс Thread с ADO.NET. Это не рекомендуемый инструмент для выполнения работы, но он не устарел. На его основе расположены другие инструменты, облегчающие работу.

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

Кайл Трауберман
источник
3

Это определенно не устарело.

Проблема с многопоточными приложениями заключается в том, что их очень сложно сделать правильно (часто важно недетерминированное поведение, ввод, вывод, а также внутреннее состояние), поэтому программист должен переносить как можно больше работы на фреймворк / инструменты. Отбросьте это прочь. Но смертельный враг абстракции - производительность.

Итак, мой вопрос: есть ли случаи, когда необходимо или полезно использовать простой старый объект Thread вместо одной из вышеуказанных конструкций?

Я бы пошел с потоками и блокировками только в том случае, если будут серьезные проблемы с производительностью, цели высокой производительности.

Лукаш Мадон
источник
3

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

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

В этом случае я не нашел лучшего способа, чем работать напрямую с классом Thread. Как упомянул Эрик Липперт, остальные - это просто абстракции более высокого уровня, и уместно работать с классами более низкого уровня, когда доступные реализации высокого уровня не соответствуют вашим потребностям. Так же, как вам иногда нужно выполнять вызовы Win32 API, когда .NET не отвечает вашим конкретным потребностям, всегда будут случаи, когда класс Thread является лучшим выбором, несмотря на недавние «достижения».

SqlRyan
источник