2013-08-14 4 views
2

Я использую потоки в своем проекте. И я хочу убить и немедленно прекратить нить.Не удается завершить потоки

образец:

type 
     test = class(TThread) 
     private 
     { Private declarations } 
     protected 
     procedure Execute; override; 
     end; 

    var 
    Form1: TForm1; 
    a:tthread; 

    implementation 

    {$R *.dfm} 

    procedure test.Execute; 
    begin 

     Synchronize(procedure begin  
      form1.ProgressBar1.position := 0; 
      sleep(5000); 
      form1.ProgressBar1.position := 100;  
     end 
    ); 

    end; 

    procedure TForm1.btn_startClick(Sender: TObject); 
    begin 
    a:=test.Create(false); 
    end; 

    procedure TForm1.btn_stopClick(Sender: TObject); 
    begin 
    terminatethread(a.ThreadID,1); //Force Terminate 
    end; 

Но когда я нажимаю на btn_stop (после нажатия на btn_start), нить не остановится. Итак, как можно немедленно остановить эту тему?

BTW a.terminate; не работал.

Спасибо.

+5

Ваша основная тема спала в течение 5 секунд, поэтому вы не можете ожидать, что она что-то предпримет. Кроме того, существует разница между 'ThreadID' и' Handle'. Функция TerminateThread ожидает поток 'Handle', а не' ThreadID'. – TLama

+0

@TLama 1) Если я закрою приложение за эти 5 секунд, как окна закрывают этот поток? 2) Это образец. Я не могу прекратить нить даже без «сна» ... И о вашем редактировании: «Как я могу это прекратить? И как я могу найти дескриптор потока? – Sky

+0

Назовите его 'TerminateThread (a.Handle, 1);'. Вы проходили там «ThreadID», который отличается от «Handle». Но обратите внимание, что это неправильный путь для прекращения потоков. – TLama

ответ

9

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

Правильное использование рабочего потока будет выглядеть следующим образом, вместо:

type 
    test = class(TThread) 
    private 
    { Private declarations } 
    protected 
    procedure Execute; override; 
    end; 

var 
    Form1: TForm1; 
    a: test = nil; 

implementation 

{$R *.dfm} 

procedure test.Execute; 
var 
    I: integer 
begin 
    Synchronize(
    procedure begin  
     form1.ProgressBar1.Position := 0; 
    end 
); 

    for I := 1 to 5 do 
    begin 
    if Terminated then Exit; 
    Sleep(1000); 
    if Terminated then Exit; 
    Synchronize(
     procedure begin 
     Form1.ProgressBar1.Position := I * 20; 
     end 
    ); 
    end; 

    Synchronize(
    procedure begin 
     form1.ProgressBar1.Position := 100;  
    end 
); 
end; 

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); 
begin 
    btn_stopClick(nil); 
end; 

procedure TForm1.btn_startClick(Sender: TObject); 
begin 
    if a = nil then 
    a := test.Create(False); 
end; 

procedure TForm1.btn_stopClick(Sender: TObject); 
begin 
    if a = nil then Exit; 
    a.Terminate; 
    a.WaitFor; 
    FreeAndNil(a); 
end; 
+0

Что делает 'a.WaitFor;' делать? И могу ли я использовать TerminateThread (a.Handle, 1); 'вместо' a.Terminate; '? Спасибо за код. – Sky

+0

_Вы делегируете всю работу потока в основной поток_ Почему? Из-за 'Синхронизация'? – Sky

+1

Да, из-за синхронизации. Это то, что делает Synchronize. Он запускает заданный код в основном потоке.Если ваш основной поток работает * этот * код, то он, очевидно, ничего не делает в то же время, особенно не обрабатывая события нажатия кнопки. –

1

Проблема заключается поток ожидает от использования Sleep. Этот метод будет поддерживать поток в течение указанного времени независимо от того, что происходит вокруг него. Чтобы иметь возможность «сломать сон», вы должны использовать событие. Код должен быть изменен следующим образом:

procedure test.Execute; 
begin 
    Synchronize(procedure begin  
     form1.ProgressBar1.position := 0; 
    end); 
    Event.WaitFor(5000); 
    if not IsTerminated then 
    Synchronize(procedure begin  
     form1.ProgressBar1.position := 100;  
    end); 
end; 

событие должно создаваться и уничтожаться, как это:

constructor test.Create(aCreateSuspended: Boolean); 
begin 
    inherited; 
    Event := TSimpleEvent.Create; 
end; 

destructor test.Destroy; 
begin 
    FreeAndNil(Event); 
    inherited; 
end; 

Для того, чтобы остановить поток, код:

procedure TForm1.btn_stopClick(Sender: TObject); 
begin 
    a.Terminate; 
end; 

Но просто вызов Terminate не будет сигнализировать о событии, поэтому мы должны переопределить Terminate:

procedure test.Terminate; 
begin 
    inherited; 
    Event.SetEvent; 
end; 

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

+0

Вам нужен деструктор, чтобы уничтожить Event, и вы должны наследовать сначала в конструкторе. –

+0

@DavidHeffernan: порядок 'inherited' в конструкторе не имеет значения. В отличие от C++, где сначала должны быть вызваны конструкторы предков, Delphi не имеет этого ограничения. Конструктор предков можно вызывать в начале, конце или даже в середине производного конструктора. –

+0

@ AlexSC: да, с помощью ожидаемого события лучше выбрать сон. Я не хотел путать Небо с этой деталью в моем ответе, поскольку он, очевидно, новичок в программировании потоков. –

Смежные вопросы