Что не так с использованием TThread.Resume?

Давным-давно, когда я начал работать с потоками в Delphi, я заставлял потоки запускаться сами, вызывая TThread.Resume в конце своего конструктора, и до сих пор так:

constructor TMyThread.Create(const ASomeParam: String);
begin
  inherited Create(True);
  try
    FSomeParam:= ASomeParam;
    //Initialize some stuff here...
  finally
    Resume;
  end;
end;

С тех пор Resume устарело, вместо него используется Start. Однако Start можно вызывать только извне потока и нельзя вызывать из конструктора.

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

FMyThread := TMyThread.Create(SomeParamValue);
FMyThread.Start;

Вопрос: По какой причине было внесено это изменение? Я имею в виду, что такого неправильного в использовании Resume, что вместо этого они хотят, чтобы мы использовали Start?

EDIT После ответа Sedat, я думаю, это действительно зависит от того, когда в конструкторе поток действительно начинает выполняться.


person Jerry Dodge    schedule 14.03.2016    source источник
comment
Окончательно неправильно. Что, если возникнет исключение?   -  person David Heffernan    schedule 15.03.2016
comment
@David Обычно я делаю здесь гораздо больше, это всего лишь примитивный пример, демонстрирующий масштаб вопроса. Простое назначение переменных наверняка не вызовет исключения, то, что у меня уже есть, уже больше, чем нужно.   -  person Jerry Dodge    schedule 15.03.2016
comment
Неважно, что вы делаете, это всегда неправильно. Не запускайте поток на частично построенном объекте, деструктор которого вот-вот запустится.   -  person David Heffernan    schedule 15.03.2016
comment
Подумайте, что делать: 1. Удалите попытку/наконец, 2. Удалите вызов Resume, 3. Передайте False унаследованному конструктору. 4. Пусть TThread.AfterConstruction запустит поток, когда все конструкторы, даже производные конструкторы, будут завершены.   -  person David Heffernan    schedule 15.03.2016
comment
Всегда мудро смотреть на источник, как это реализовано. Текущая реализация также учитывает другое поведение Windows 2003 Server, запускающего поток в вызове CreateThread   -  person Sir Rufo    schedule 15.03.2016
comment
@SirRufo Я думаю, вы неправильно читаете документацию. Во всех версиях окна, если CREATE_SUSPENDED не передается, то поток может начать выполнение до того, как CreateThread вернется. Специфическое поведение Windows Server 2003 описано в предыдущем абзаце документации и связано с маркерами и олицетворением.   -  person David Heffernan    schedule 15.03.2016
comment
Что делает это интересным, так это то, что перегруженный constructor Create(CreateSuspended: Boolean); НЕ устарел, в то время как Resume.   -  person Jerry Dodge    schedule 15.03.2016
comment
@JerryDodge Нет. Есть еще причины, по которым вы можете захотеть создать приостановленный. Затем, когда вы хотите запустить поток, вызовите Start. Просто как тот.   -  person David Heffernan    schedule 15.03.2016


Ответы (1)


Короткий и содержательный ответ заключается в том, что авторы класса TThread не доверяли разработчикам читать или понимать документацию. :)

Приостановка и возобновление потока — допустимая операция только для очень ограниченного числа случаев использования. На самом деле, это ограниченное количество по сути равно "одному": Отладчики.

Нежелательные

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

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

Отладчику нужна возможность напрямую приостанавливать поток независимо от этих механизмов по удивительно схожим причинам.

Рассмотрим, например, что точка останова включает неявную (или можно даже сказать exявную) операцию «приостановить» в потоке. Если отладчик останавливает поток, когда он достигает точки останова, он также должен приостанавливать все остальные потоки в этом процессе именно потому, что в противном случае они будут мчаться вперед, выполняя работу, которая может помешать многим низкоуровневым задачам, которые могут быть запрошены отладчиком. тогда делай.

Сильная рука отладчика

Отладчик не может «внедрить» красивые, вежливые объекты и механизмы синхронизации, чтобы запросить, чтобы эти другие потоки приостановили себя скоординированным образом с каким-либо другим потоком, который был бесцеремонно остановлен (точкой останова). У отладчика нет другого выбора, кроме как усилить потоки, и именно для этого и предназначены API Suspend/Resume.

Они предназначены для ситуаций, когда вам нужно остановить поток "Прямо сейчас. Что бы вы ни делали, мне все равно, просто остановитесь!". А позже, чтобы затем сказать: «Хорошо, теперь вы можете продолжать то, что вы делали раньше, что бы это ни было.».

Потоки с хорошим поведением ведут себя хорошо по отношению друг к другу

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

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

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

Пуск против приостановки/возобновления

Поэтому было решено, что Suspend/Resume не имеет реального места в классе потоков общего назначения (люди, реализующие отладчики, все еще могут напрямую вызывать Windows API), а вместо этого более подходящий " Предусмотрен механизм «Пуск».

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

person Deltics    schedule 15.03.2016
comment
Я бы сказал, что разработчики языков и фреймворков учатся не доверять пользователям НА ВЕЧНОЙ ПРИЧИНЕ. Посредственный разработчик, который берет переданные ему части и делает все, что РАЗРЕШАЕТ, не понимая лежащих в основе вещей, кажется, превосходит числом вдумчивых ИНЖЕНЕРОВ ПО, которые пытаются изучить всю систему полностью, примерно 10: 1 по моему опыту. Таким образом, никаких предупреждений недостаточно. Было бы неплохо, если бы был TWorkerThread, но поскольку его не было, разработчики обходились и устраивали беспорядок. Тогда они обвиняют RTL. - person Warren P; 15.03.2016
comment
Даже если бы существовал TWorkerThread, были бы способы и средства доступа к его дескриптору для передачи в API приостановки/возобновления ОС. Где ты останавливаешься? Должен ли API Windows не предоставлять API Suspend/Resume? Как же тогда кто-нибудь сможет разумно реализовать отладчик? То же самое недоверие применимо к любому количеству средств и возможностей, предоставляемых API и даже языками. - person Deltics; 15.03.2016
comment
TThread не предназначен для использования с отладчиками. Для этого подходят API Win32. - person David Heffernan; 16.03.2016
comment
Если нельзя ожидать, что разработчик будет правильно использовать TThread с возможностями приостановки/возобновления, то как можно ожидать, что он будет правильно использовать Windows Suspend/Resume API? Мне кажется, их просто нельзя подпускать к любому компилятору без присмотра взрослых. :) Плохой работник, обвиняющий свои инструменты, не означает, что инструменты нуждаются в ремонте. Плохой работник все равно будет делать плохую работу и продолжать обвинять свои инструменты. - person Deltics; 16.03.2016
comment
Я не думаю, что устаревание должно было спасти некомпетентных программистов от самих себя. Я думаю, что устаревание было для x-plat. Приостановка/возобновление только для Windows? - person David Heffernan; 16.03.2016
comment
Я обращался к вашим рассуждениям / рассуждениям тех, кто говорит, что приостановка / возобновление по своей сути плохи. относительно временной шкалы, начиная с Delphi 7, TThread имел x-plat Suspend/Resume для Windows и (почти) поддержку потоков POSIX в Linux для Kylix. Однако в реализации отмечается, что если бы Linux правильно обрабатывал потоки POSIX, обходной путь не работал бы. Но по сути соображения x-plat были на первом месте. Устаревание произошло позже. Возможно, они поняли, что не могут достичь x-plat надежно, поэтому вместо этого стремились к наименьшему общему знаменателю. :пожимаю плечами: - person Deltics; 16.03.2016
comment
Вы сейчас вкладываете слова в мои уста. Я никогда не говорил, что здесь. Хотя SuspendThread и ResumeThread предназначены только для отладчиков, а TThread предназначен для определения потоков, а не для отладки потоков, определенных другими, Suspend и Resume всегда были бессмысленны. Без сомнения, первоначальный разработчик TThread совершил ту же ошибку, что и многие другие, и сделал вывод, что SuspendThread безопасен для не-отладчиков. - person David Heffernan; 17.03.2016
comment
Бессмысленно? Если, возможно, вы не реализуете сценарий или другую динамическую среду выполнения, которая может захотеть создать потоки и отладки? Никакие слова не предназначались для того, чтобы вкладывать их в ваши уста - вы, казалось, поддерживали / следовали веским причинам Уоррена не доверять разработчикам. Извините, если это не было вашим намерением. Возможно, вы правы в том, что изначально он был включен по неправильной причине, но это не делает удаление его по неправильной причине более правильным. :) - person Deltics; 18.03.2016