15 / 15 / 14
Регистрация: 19.08.2012
Сообщений: 105
1

Непонятное поведение программы с потоками

04.10.2017, 13:56. Показов 1940. Ответов 17
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Решил начать с примитива и сразу же проблемы!
Читаю в переводе книгу Мартина Харвей по Многопоточности и первый же пример глючит.
Программа должна определять, является ли число простым. При вымполнении программы происходит что-то непонятно:
1.Если введено не целое число не отрабатывает исключение:
Вложение 869894Вложение 869895
2.Если значение целое число не всегда появляется сообщение из потока. Хотя поток создаётся, вычисляет и завершается.
Вложение 869896
3.В режиме отладки, когда ставлю BreakPoint сообщение появляется.
Вложение 869897

В чём беда? В настройках компилятора? Использую Delphi 2010.

Код основной формы:
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
unit PrimeForm;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
 
type
  TPrimeFrm = class(TForm)
    NumEdit: TEdit;
    SpawnButton: TButton;
    procedure SpawnButtonClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  PrimeFrm: TPrimeFrm;
 
implementation
 
uses PrimeThread;
 
{$R *.dfm}
 
procedure TPrimeFrm.SpawnButtonClick(Sender: TObject);
var NewThread:TPrimeThrd;
begin
  NewThread:=TPrimeThrd.Create(True);
  NewThread.FreeOnTerminate:=True;
  try
    NewThread.TestNumber:=StrToInt(NumEdit.Text);
    NewThread.Resume;
  except on EConvertError do
    begin
      NewThread.Free;
      ShowMessage(NumEdit.Text + ' - Это не положительное целое число!');
    end;    
  end;
end;
 
end.
Код потока:
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
unit PrimeThread;
 
interface
 
uses Classes;
 
type
  TPrimeThrd = class(TThread)
  private
    FTestNumber: integer;
  protected
    function IsPrime: boolean;
    procedure Execute; override;
  public
    property TestNumber: integer write FTestNumber;
  end;
 
implementation
 
uses SysUtils, Dialogs;
 
function TPrimeThrd.IsPrime: boolean;
Var iter:integer;
begin
  Result:=True;
  if FTestNumber<0 then
  begin
    Result:=False;
    Exit;
  end;
 
  if FTestNumber<=2 then Exit;
 
  for iter := 2 to FTestNumber - 1 do
  if (FTestNumber mod iter) =0 then
  begin
    Result:=False;
    {Exit;}
  end;
end;
 
procedure TPrimeThrd.Execute;
begin
  if IsPrime then
    ShowMessage(IntToStr(FTestNumber) + ' - простое число')
  else
    ShowMessage(IntToStr(FTestNumber) + ' - не простое число');
end;
 
end.
1
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
04.10.2017, 13:56
Ответы с готовыми решениями:

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

Непонятное поведение процедуры
Добрый день уважаемый форумчане. Имеется процедура, она работает с string_grid, парой...

MD5 непонятное поведение
Доброе утро, Помогите разобрать MD5. Есть формула : ss1 = md5(data + password) password -...

Непонятное поведение getera property
Помогите разобраться, что делаю не так Имеется следующий класс CPointFromLine = class (CPoint) ...

17
5656 / 4418 / 1409
Регистрация: 14.04.2014
Сообщений: 19,772
Записей в блоге: 20
04.10.2017, 13:58 2
Цитата Сообщение от FearDog Посмотреть сообщение
procedure TPrimeThrd.Execute;
begin
* if IsPrime then
* * ShowMessage(IntToStr(FTestNumber) + ' - простое число')
* else
* * ShowMessage(IntToStr(FTestNumber) + ' - не простое число');
end;
вот ни за что не поверю что ShowMessage вдруг внутри потока
0
15 / 15 / 14
Регистрация: 19.08.2012
Сообщений: 105
04.10.2017, 14:09  [ТС] 3
Это как первый пример в книге и неправильный вариант реализации. Но самое интересное, что с BreakPoint'ом работает))
Непонятное поведение программы с потоками


А почему ShowMessage не должен отрабатывать в потоке?
0
15 / 15 / 14
Регистрация: 19.08.2012
Сообщений: 105
04.10.2017, 14:47  [ТС] 4
И почему не срабатывает Try except end;?
Название: Форма.jpg
Просмотров: 65

Размер: 7.9 КбНазвание: Ошибка.jpg
Просмотров: 68

Размер: 10.8 Кб
0
5656 / 4418 / 1409
Регистрация: 14.04.2014
Сообщений: 19,772
Записей в блоге: 20
04.10.2017, 16:30 5
потому что поток вообще не должен бы иметь никакого интерфейса
если запустить 5 потоков, они засыплют экран диалогами, при этом еще и главный поток будет работать ))

а с чего должен бы сработать except ?
try
создать поток - создался норм
сказали потоку запустись - запустился норм
except ...
end
программа спокойно работает дальше

Execute потока будет работать совершенно отдельно. Вне рамок try-except
0
15 / 15 / 14
Регистрация: 19.08.2012
Сообщений: 105
05.10.2017, 06:17  [ТС] 6
Цитата Сообщение от krapotkin Посмотреть сообщение
а с чего должен бы сработать except ?
try
создать поток - создался норм
сказали потоку запустись - запустился норм
except ...
end
программа спокойно работает дальше
У меня то немного по-другому, поток создаётся до try, а в try передаётся значение переменной, и если возникает ошибка, поток должен освобождаться и выскакивать моё сообщение об ошибке, а выскакивает системное при передаче переменной:

Delphi
1
2
3
4
5
6
7
8
9
10
11
12
  
  NewThread:=TPrimeThrd.Create(True);
  NewThread.FreeOnTerminate:=True;
  try
    NewThread.TestNumber:=StrToInt(NumEdit.Text); 'Тут ошибка'
    NewThread.Resume;
  except on EConvertError do
    begin
      NewThread.Free;
      ShowMessage(NumEdit.Text...);
    end;    
  end;
0
5656 / 4418 / 1409
Регистрация: 14.04.2014
Сообщений: 19,772
Записей в блоге: 20
05.10.2017, 08:09 7
еще раз
Resume - запускает поток и все. программа идет дальше, переходя через except
если вы хотите ловить exception в потоке, помещайте его в Execute
0
15 / 15 / 14
Регистрация: 19.08.2012
Сообщений: 105
05.10.2017, 08:46  [ТС] 8
Цитата Сообщение от krapotkin Посмотреть сообщение
Resume - запускает поток и все. программа идет дальше, переходя через except
Так не доходит программа до создания потока.
Delphi
1
2
3
4
5
6
7
8
9
10
11
NewThread:=TPrimeThrd.Create(True); 'Создали поток'
  NewThread.FreeOnTerminate:=True; 'Задали параметр'
  try
    NewThread.TestNumber:=StrToInt(NumEdit.Text); 'Попробовали в Integer запихнуть String - Ошибка'
    NewThread.Resume; 'Не отрабатывается'
  except on EConvertError do
    begin
      NewThread.Free; 'Не отрабатывается'
      ShowMessage(NumEdit.Text...); 'Не отрабатывается'
    end;    
  end;
После четвёртой строчки исполнение кода прерывается, ошибка! Но почему-то, вместо того, чтобы после ошибки в блоке try перейти в блок except, программа выходит из процедуры. А нужно то, что бы при неправильном вводе поток удалялся и выходила Моя ошибка:
Delphi
1
ShowMessage(NumEdit.Text + ' - Это не положительное целое число!');
P.S.: Даже если коментю //NewThread.Resume; - ничего не меняется.
0
5656 / 4418 / 1409
Регистрация: 14.04.2014
Сообщений: 19,772
Записей в блоге: 20
05.10.2017, 09:38 9
сорри, дошло, что ошибку вы вызываете до потока
чудес не бывает
возможно, вы привели не весь код
тогда все должно так же работать если убрать не только Resume но и весь поток вообще
0
15 / 15 / 14
Регистрация: 19.08.2012
Сообщений: 105
05.10.2017, 10:00  [ТС] 10
krapotkin, это весь код. Прога в две строчки. Может глюки компилятора, или настройки проги есть какие?
Я в архиве оставил скомпилированный мной проект. Такая же ошибка выходит?
Вложения
Тип файла: zip Prime.zip (402.5 Кб, 4 просмотров)
0
5656 / 4418 / 1409
Регистрация: 14.04.2014
Сообщений: 19,772
Записей в блоге: 20
05.10.2017, 10:31 11
Delphi
1
2
3
4
5
6
7
8
9
  try
    num:=strToInt(NumEdit.text);
  except on E:Exception do
      ShowMessage(NumEdit.Text + ' - не положительное целое число!');
  end;
 
  NewThread:=TPrimeThrd.Create(True);
  NewThread.FreeOnTerminate:=True;
  NewThread.TestNumber:=num;
1
15 / 15 / 14
Регистрация: 19.08.2012
Сообщений: 105
05.10.2017, 12:40  [ТС] 12
Нашёл проблему - программа ругается на NewThread.Free; в части except

В первой стоке идёт создание потока. Почему компилятор ругается на NewThread.Free?
Даже в такой связке ругается:
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
var   NewThread:TPrimeThrd; 'Глобальная переменная'
 
procedure TPrimeFrm.SpawnButtonClick(Sender: TObject);
begin
  NewThread:=TPrimeThrd.Create(True);
  NewThread.FreeOnTerminate:=True;
end;
 
procedure TPrimeFrm.SpawnButton2Click(Sender: TObject);
begin
  NewThread.Free; 'Ругается!'
end;
Но если убрать NewThread.FreeOnTerminate:=True, то всё норм.
Delphi
1
2
3
4
5
6
7
8
9
procedure TPrimeFrm.SpawnButtonClick(Sender: TObject);
begin
  NewThread:=TPrimeThrd.Create(True);
end;
 
procedure TPrimeFrm.SpawnButton2Click(Sender: TObject);
begin
  NewThread.free;
end;
И такой код тоже работает без ошибок:
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
procedure TPrimeFrm.SpawnButtonClick(Sender: TObject);
var NewThread:TPrimeThrd;
begin
  NewThread:=TPrimeThrd.Create(True);
  try
    NewThread.TestNumber:=StrToInt(NumEdit.Text);
    NewThread.Resume;
  except on EConvertError do
    begin
      NewThread.Free;
      ShowMessage(NumEdit.Text + ' - Это не положительное целое число!');
    end;
  end;
end;
krapotkin, как профи по потокам, можешь объяснить, почему с FreeOnTerminate:=True метод Free выдаёт ошибку? И как правильно уничтожать потоки с FreeOnTerminate:=True?

Не по теме:

P.S.: Я согласен, что нефиг создавать поток, если велика вероятность, что придётся его тут же уничтожать. В данном примере моя цель не рабочая программа, а разобраться в логике работы потока. И если есть проблемы с созданием и остановкой, то дальше идти просто безответственно и в нормальных проектах будут большие проблемы:scratch:

0
500 / 346 / 200
Регистрация: 20.10.2016
Сообщений: 1,101
05.10.2017, 12:59 13
Это вообще уберите
Цитата Сообщение от FearDog Посмотреть сообщение
NewThread.Free;
и оставьте так
Цитата Сообщение от FearDog Посмотреть сообщение
NewThread.FreeOnTerminate:=True
Если поток поймает ексепшен он завершится и освободится самостоятельно.

Добавлено через 1 минуту
Если НЕ поймает, то после завершения сам уничтожится.
0
15 / 15 / 14
Регистрация: 19.08.2012
Сообщений: 105
05.10.2017, 13:14  [ТС] 14
TFullControl, в подобном коде поток даже не успевает запуститься:
Delphi
1
2
3
4
5
6
7
8
9
10
NewThread:=TPrimeThrd.Create(True); 'Создали поток'
  NewThread.FreeOnTerminate:=True; 'Задали параметр'
  try
    NewThread.TestNumber:=StrToInt(NumEdit.Text); 'Попробовали в Integer запихнуть String - Ошибка'
    NewThread.Resume; 'Не отрабатывается'
  except on EConvertError do
    begin
      ShowMessage(NumEdit.Text...); 
    end;    
  end;
Т.е. поток создан, не запущен, висит в состоянии ожидания, или я что-то не так понимаю? Он освободиться же только после отработки процедуры procedure TPrimeThrd.Execute? Но при ошибке этого не будет.
0
500 / 346 / 200
Регистрация: 20.10.2016
Сообщений: 1,101
05.10.2017, 14:22 15
FearDog, читайте посто №11 от крапоткина, он уже дал вам ответ как в таком случае правильно создавать поток и обрабатывать исключения.
0
5656 / 4418 / 1409
Регистрация: 14.04.2014
Сообщений: 19,772
Записей в блоге: 20
05.10.2017, 16:39 16
Лучший ответ Сообщение было отмечено FearDog как решение

Решение

для того, чтобы понять механизм, просто в своем классе переопределите
Delphi
1
2
3
4
5
6
destructor Destroy; override;
...
destructor TNewThread.Destroy; 
begin
  inherited;
end;
и поставьте breakpoint на строчку inherited;
и вы узнаете, когда уничтожился ваш поток...
1
15 / 15 / 14
Регистрация: 19.08.2012
Сообщений: 105
06.10.2017, 13:02  [ТС] 17
Цитата Сообщение от TFullControl Посмотреть сообщение
FearDog, читайте посто №11 от крапоткина, он уже дал вам ответ как в таком случае правильно создавать поток и обрабатывать исключения.
Просто бездумно создать рабочий вариант - не сама цель. Код из первого поста отрабатывает в Delphi 7, но категорически отказывается работать в Delphi 2010. Я хочу понять не как нужно (это я уже понял), а причину, почему нельзя.

Цитата Сообщение от krapotkin Посмотреть сообщение
для того, чтобы понять механизм, просто в своем классе переопределите
Delphi
1
2
3
4
5
6
destructor Destroy; override;
...
destructor TNewThread.Destroy; 
begin
* inherited;
end;
и поставьте breakpoint на строчку inherited;
и вы узнаете, когда уничтожился ваш поток...
Спасибо за наводку. Буду копаться и изучать поведение потока при создании/ошибке/разрушении)
И Ваш пост в блоге тоже прочту в свободное время)

Добавлено через 5 часов 2 минуты
Изучил поведение программы и вот что заметил:Если FreeOnTerminate:=True, то освобождение потока методом Free вызывает Destroy дважды. Я извратился над кодом и пришёл к выводу, что если FreeOnTerminate:=True, то перед уничтожением потока нужно присваивать FreeOnTerminate:=False, и лишь потом вызывать Free. Такой код работает как задумано:
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
procedure TPrimeFrm.SpawnButtonClick(Sender: TObject);
var NewThread:TPrimeThrd;
begin
  NewThread:=TPrimeThrd.Create(True);
  NewThread.FreeOnTerminate:=True;
  try
    NewThread.TestNumber:=StrToInt(NumEdit.Text);
    NewThread.Resume;
  except on EConvertError do
    begin
      NewThread.FreeOnTerminate:=False;
      NewThread.Free;
      ShowMessage(NumEdit.Text + ' - Это не положительное целое число!');
    end;
  end;
end;
Порылся в модулях Delphi 7, не нарыл ничего интересного:
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
{System.pas}
procedure TObject.Free;
begin
  if Self <> nil then
    Destroy;
end;
 
destructor TObject.Destroy;
begin
end;
 
procedure TObject.AfterConstruction;
begin
end;
 
procedure TObject.BeforeDestruction;
begin
end;
 
{Classes.pas}
 
constructor TThread.Create(CreateSuspended: Boolean);
begin
  inherited Create;
  AddThread;
  FSuspended := CreateSuspended;
  FCreateSuspended := CreateSuspended;
  FHandle := BeginThread(nil, 0, @ThreadProc, Pointer(Self), CREATE_SUSPENDED, FThreadID);
  if FHandle = 0 then
    raise EThread.CreateResFmt(@SThreadCreateError, [SysErrorMessage(GetLastError)]);
end;
 
function ThreadProc(Thread: TThread): Integer;
var
  FreeThread: Boolean;
begin
  try
    if not Thread.Terminated then
    try
      Thread.Execute;
    except
      Thread.FFatalException := AcquireExceptionObject;
    end;
  finally
    FreeThread := Thread.FFreeOnTerminate;
    Result := Thread.FReturnValue;
    Thread.DoTerminate;
    Thread.FFinished := True;
    SignalSyncEvent;
    if FreeThread then Thread.Free;
    EndThread(Result);
  end;
end;
 
destructor TThread.Destroy;
begin
  if (FThreadID <> 0) and not FFinished then
  begin
    Terminate;
    if FCreateSuspended then
      Resume;
    WaitFor;
  end;
  if FHandle <> 0 then CloseHandle(FHandle);
  inherited Destroy;
  FFatalException.Free;
  RemoveThread;
end;
Вызов Free, в случае непустого Self просто вызывает Destroy. Так почему он срабатывает дважды? Выполняется ещё какая-то процедура перед Destroy? BeforeDestruction? Но она не переопределяется...
Но это всё в Delphi 7. krapotkin, как можно посмотреть исходники Classes.dcu в поздних версиях?
0
5656 / 4418 / 1409
Регистрация: 14.04.2014
Сообщений: 19,772
Записей в блоге: 20
06.10.2017, 13:59 18
эммм ???
просто если FreeOnTerminate=true то нет никакой надобности вызывать Free
0
06.10.2017, 13:59
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
06.10.2017, 13:59
Помогаю со студенческими работами здесь

Непонятное поведение TEdit
Здравствуйте! Если имеется минутка времени, посмотрите, пожалуйста, фрагмент простейшего кода: ...

Непонятное поведение программы
Привет народ, вот кароч: #include&quot;stdafx.h&quot; #include&quot;iostream&quot; using namespace std; class calc{...

Непонятное поведение программы
Пишу статическую либу. В ней есть кусок кода, который уходит в рекурсию.. Только причины мне...

Непонятное поведение программы
Здравствуйте, уважаемые форумчане! Столкнулся со следующей проблемой: Исключение: ...

Непонятное поведение программы
Доброго времени суток. Уважаемые форумчане, очень нуждаюсь в вашей консультации насчёт своей...

Непонятное поведение программы!
есть счетчик, который выводит цифры в TextView (подобие секундомера) есть кнопка запуска, она же...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
18
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2023, CyberForum.ru