Правильное завершение работы потока

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by mailbrush, 20 Jul 2011.

  1. mailbrush

    mailbrush Well-Known Member

    Joined:
    24 Jun 2008
    Messages:
    1,997
    Likes Received:
    996
    Reputations:
    155
    Здравствуйте. Есть поток, который в цикле делает кое-какую работу (взаимодействует с сетью). Когда подана команда его завершения - свойство Terminated становится true. Таким образом, чтобы завершить работу потока, нужно в цикле его работы проверять значение этого свойства.

    Code:
    while(true)
    {
    	if(this->Terminated)
    		{
    			...
    			...
    			...
    		}
    	
    	...
    	...
    	...
    }
    Но всё дело в том, что время итерации кода может достигать нескольких минут, а завершить всё надо быстро. Поэтому, возникала идея проверять Terminated при каждой отправке данных на сервер. Будет ли рационально делать так? Ибо таких вот "отправок" есть очень много, и после каждой вставлять этот код мне кажется не очень правильным решением.
     
  2. tim-oleksii

    tim-oleksii Member

    Joined:
    14 Mar 2011
    Messages:
    199
    Likes Received:
    10
    Reputations:
    0
    WaitForSingleObject не годится? Сам ждет терминирования, ждет указанное время.

    Я бы в главном потоке (поток-арбитр) сделал ожидание завершения других, а весь остальной код разбил бы по другим потокам.
     
    #2 tim-oleksii, 20 Jul 2011
    Last edited: 20 Jul 2011
  3. mailbrush

    mailbrush Well-Known Member

    Joined:
    24 Jun 2008
    Messages:
    1,997
    Likes Received:
    996
    Reputations:
    155
    Не то. WaitForSingleObject ждёт завершения потока.

    А мне нужно, чтобы поток завершился по моему желанию, тоесть в любой момент времени. В классе TThread есть свойство функция Terminate, которая присваивает флажку Terminated значение true. Но где именно его проверять, чтобы завершение прошло быстро и рационально?
     
  4. alexey-m

    alexey-m Elder - Старейшина

    Joined:
    15 Jul 2009
    Messages:
    518
    Likes Received:
    100
    Reputations:
    37
    эм, а TerminateThread(hThread, 0); и CloseHandle(hThread); не подходит?
     
  5. xophet

    xophet Member

    Joined:
    16 Apr 2011
    Messages:
    617
    Likes Received:
    49
    Reputations:
    5
    в общем по телу потока, в его функциях и процедурах делаешь проверку
    Code:
    if terminated then exit
     
  6. mailbrush

    mailbrush Well-Known Member

    Joined:
    24 Jun 2008
    Messages:
    1,997
    Likes Received:
    996
    Reputations:
    155
    TerminateThread никак не подходит, потому что он завершает поток, а все объекты так и остаются "плавать" в памяти программы.

    Об этом я испрашивал, перечитай пост. Мне кажется, будет нерационально после каждой функции проверять terminated...
     
  7. Chrome~

    Chrome~ Elder - Старейшина

    Joined:
    13 Dec 2008
    Messages:
    937
    Likes Received:
    162
    Reputations:
    27
    Обычно значение поля Terminated проверяют в начале цыкла. Но если ты говоришь, что проход по цыклу может занимать длительное время, то все таки лучше делать проверку Terminated в нескольких местах кода.

    Мне кажеться, что это не правильно, если пользователь нажмет в программе кнопку "Стоп" и все потоки немедленно завершаться (то есть убить всех каким то способом из главного). Лучше делать именно проверку Terminated и завершать в потоке "самого себя" так, как надо.
     
  8. mailbrush

    mailbrush Well-Known Member

    Joined:
    24 Jun 2008
    Messages:
    1,997
    Likes Received:
    996
    Reputations:
    155
    Понятное дело, поэтому и не использую TerminateThread и ей подобные.
    Я не говорил, что проверка Terminated - неправильна.
    Просто проверять её во многих местах мне кажется как минимум - некорректо.
     
  9. Chrome~

    Chrome~ Elder - Старейшина

    Joined:
    13 Dec 2008
    Messages:
    937
    Likes Received:
    162
    Reputations:
    27
    Лучше всего проверять значение Terminated перед отправкой следующего запроса. Если тебя смущает большое количество проверок, можно поступить следующим образом. Если ты используешь ООП в своей работе, то можешь настроить события для компонента, например, который отвечает за работу с сетью. У всех нормальных компонентов/классов присутсвуют события, которые возникают перед стартом работы. Так вот можешь запрограммировать эти события, и твой код не только начнет смотреться читабельние, но и все будет правильно работать.
     
    #9 Chrome~, 20 Jul 2011
    Last edited: 20 Jul 2011
  10. mailbrush

    mailbrush Well-Known Member

    Joined:
    24 Jun 2008
    Messages:
    1,997
    Likes Received:
    996
    Reputations:
    155
    А я вот не суеверен. Качать файл 100МБ "одной строкой" да ещё и через инди - убожество :D
    У меня есть несколько вложенных циклов, которые выполняют кое-какую работу. В основном вся задержка - на приём и отправку запросов (их много). Поэтому в начало рабочего цикла не вариант. Тогда, всё же, буду использовать проверку в теле каждого вложенного цикла.
     
  11. Jingo Bo

    Jingo Bo Member

    Joined:
    25 Oct 2009
    Messages:
    368
    Likes Received:
    51
    Reputations:
    7
    Code:
    Var S : TFileStream;
    Begin
      S := TFileStream.Create('C:\temp.html', fmCreate);
      try
        IdHTTP.Get('http://site.ru', S);
      finally
        S.Free;
    end;
    Фактически одной строкой, в чем же тут убожество?) Indy хорошая библиотека. Да...раньше багов было много, но щас практически все поправили(Я о 10-ой версии, к примеру в Delphi XE). Чтобы через определенное количество байт делать проверку при загрузки - можно использовать событие OnWork.

    На счет проверок, я бы сделал так :
    Code:
    type
      TThreadTerminateException = class(Exception);
    
    procedure TMyThread.Execute;
      procedure CheckTerminate;
      Begin
        if Terminated then
          raise TThreadTerminateException.Create('');
      end;
    Begin
      try
        try
          //Основной код потока
          //...
          CheckTerminate; //Проверка на завершение потока.
          //...
        finally
          //действия, выполняемые в конце потока
        end;
      except
        on e1:TThreadTerminateException
        Begin
          //действия, если поток завершился аварийно
        end;
        on e2:Exception
        Begin
          //Выполнить действия, если просто произошло не определенное исключение
        end;
      end;
    end;
     
  12. mailbrush

    mailbrush Well-Known Member

    Joined:
    24 Jun 2008
    Messages:
    1,997
    Likes Received:
    996
    Reputations:
    155
    Спасибо. Думаю, это оптимальный вариант.
    Буду юзать исключения :)
     
  13. Jingo Bo

    Jingo Bo Member

    Joined:
    25 Oct 2009
    Messages:
    368
    Likes Received:
    51
    Reputations:
    7
    CheckTerminate можно вообщем вывести в сам класс потока и если вызываешь внешние функции внутри потока, то в них можно так же передавать ссылку на данный класс потока и собсна уже в них проверять активность.
     
  14. GhostOnline

    GhostOnline Active Member

    Joined:
    20 Dec 2008
    Messages:
    723
    Likes Received:
    110
    Reputations:
    22
    Подобные проблемы исчезают, если юзать ООП =)
    Сколько у тебя строк в теле цикла? Небось 200-300? :)
     
  15. Jingo Bo

    Jingo Bo Member

    Joined:
    25 Oct 2009
    Messages:
    368
    Likes Received:
    51
    Reputations:
    7
    Ну ка, ну ка?)
     
  16. GhostOnline

    GhostOnline Active Member

    Joined:
    20 Dec 2008
    Messages:
    723
    Likes Received:
    110
    Reputations:
    22
    Как я понял у ТСа код наподобие этого:
    while (true)
    {
    // тут строк 10 низкоуровной работы с сетью
    // тут проверка
    // еще строк 10 низкоуровной работы с сетью
    // тут опять проверка
    // тут вложенный цикл и в нем еще много таких подобных участков
    }
    что по мне есть аццкий быдлокот
    ибо все эти участки весьма похожи. это дублирование кода
    я бы вынес выполнение запросов в отдельный класс, куда передается параметрами то что нужно отправить. а в этот класс внес событие, что-то вроде OnAfterRequest, соответственно вызываемое после каждого запроса. Код работающий с классом подписывается на него, где проверяет на Terminated, если true то генерится ексепшн, ибо это единственный способ выйти и из вызывающих методов.
    Но, судя по некоторым репликам ТСу глубоко начхать на good practices, он и будет дальше писать огромные циклы и дублировать однотипный код.
     
  17. Jingo Bo

    Jingo Bo Member

    Joined:
    25 Oct 2009
    Messages:
    368
    Likes Received:
    51
    Reputations:
    7
    А мне кажется что это перебор, если пишешь какой нить универсальный код из стопицот модулей, который можно будет использовать для множества прог, то да. Нужно писать относительно задачи, а не раздувать...раздувать если проект на много лет, а не ради красоты кода.
    Без этого в данной задаче никак, будешь дублировать текст - профит от раздувательства будет не лучше.