Перейти из форума на сайт.

НовостиФайловые архивы
ПоискАктивные темыТоп лист
ПравилаКто в on-line?
Вход Забыли пароль? Первый раз на этом сайте? Регистрация
Компьютерный форум Ru.Board » Компьютеры » Прикладное программирование » Зависание программы и TThread

Модерирует : ShIvADeSt

 Версия для печати • ПодписатьсяДобавить в закладки
Страницы: 1 2

Открыть новую тему     Написать ответ в эту тему

Steepe_Hare



Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Вот простой код, которые работает отлично под Виндами и зависает под Андроидом:
 
  i: Integer;
  Finish: Boolean;
 
implementation
 
{$R *.fmx}
 
procedure TForm1.Button1Click(Sender: TObject);
begin
  i := 0;
  Finish := False;
  Memo1.Lines.Add('Start');
  Button2.Enabled := True;
 
  while not Finish do
    Application.ProcessMessages;
 
  Memo1.Lines.Add('Finish');
end;
 
procedure TForm1.Button2Click(Sender: TObject);
begin
  Inc(i);
  Memo1.Lines.Add(IntToStr(i));
 
  if (i = 5) then
  begin
    Finish := True;
    Button2.Enabled := False;
  end;
end;
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  Button2.Enabled := False;
end;
 
 
Посоветовали пользоваться TThread, но я еще не умею(  Как бы корректно переписать данный код с использованием потоков? Спасибо

Всего записей: 1162 | Зарегистр. 27-10-2001 | Отправлено: 10:03 01-12-2016
ShIvADeSt



Moderator
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Попробуйте TTimer, он проще. Interval примерно 300-500 мс сделайте.
В процедуре OnTimer

Код:
 
if Finish then begin
    Memo1.Lines.Add('Finish');
    Timer1.Endabled := False;
end;
Application.ProcessMessages;  
 

 
тогда первая процедура будет

Код:
procedure TForm1.Button1Click(Sender: TObject);
begin
  i := 0;
  Finish := False;
  Memo1.Lines.Add('Start');
  Button2.Enabled := True;
  Timer1.Enabled := True;
end;  



----------
И создал Бог женщину... Существо получилось злобное, но забавное...

Всего записей: 3956 | Зарегистр. 29-07-2003 | Отправлено: 10:15 01-12-2016 | Исправлено: ShIvADeSt, 10:16 01-12-2016
Steepe_Hare



Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Задача: дождаться действий пользователя (допустим, 5 раз нажать на Button2) и только потом выполнять следующий после этого код
 
 while not Finish do
    Application.ProcessMessages;

Всего записей: 1162 | Зарегистр. 27-10-2001 | Отправлено: 13:35 01-12-2016 | Исправлено: Steepe_Hare, 13:39 01-12-2016
Dronton2

Full Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Steepe_Hare
Может быть, использовать сообщения?
Нажали 5 раз кнопку - отправить сообщение.
И добавить обработчик этого сообщения.

Всего записей: 460 | Зарегистр. 27-06-2005 | Отправлено: 15:33 01-12-2016
Alexzzy

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Steepe_Hare

Цитата:
 Как бы корректно переписать данный код с использованием потоков?  

Изучить вопрос по хорошей литературе. Например - Стив Тейксейра, Ксавье  Пачеко - "Дельфи  5 . Руководство разработчика."

Всего записей: 1474 | Зарегистр. 10-07-2013 | Отправлено: 17:43 01-12-2016
LadyOfWood

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Steepe_Hare
По мне тут логику менять надо, тут явно лишнии и потоки и ProcessMessages.

Всего записей: 620 | Зарегистр. 16-09-2003 | Отправлено: 17:45 01-12-2016
Dronton2

Full Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Чтобы поменять логику приложения, нужно знать, на какую логику нужно поменять, а для этого - нужно знать, как сформулирована задача. В том виде, в котором её сформулировал Steepe_Hare
Цитата:
Задача: дождаться действий пользователя (допустим, 5 раз нажать на Button2) и только потом выполнять следующий после этого код  

, простейшим решением будет поменять код двух процедур, на следующий:

Код:
procedure TForm1.Button1Click(Sender: TObject);  
begin  
  i := 0;  
  Memo1.Lines.Add('Start');  
  Button2.Enabled := True;  
end;  
 
procedure TForm1.Button2Click(Sender: TObject);  
begin  
  Inc(i);  
  Memo1.Lines.Add(IntToStr(i));  
 
  if (i = 5) then  
  begin  
    Button2.Enabled := False;  
    Memo1.Lines.Add('Finish');  
  end;  
end;
Но, если советуют использовать потоки, то задача должна быть сложнее.
Имхо, обмениваться информацией между потоками правильнее всего - с помощью сообщений.
Кстати, те же таймер и кнопки - тоже ведь на сообщениях работают.

Всего записей: 460 | Зарегистр. 27-06-2005 | Отправлено: 18:18 01-12-2016
Alexzzy

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Dronton2

Цитата:
Имхо, обмениваться информацией между потоками правильнее всего - с помощью сообщений.  

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

Всего записей: 1474 | Зарегистр. 10-07-2013 | Отправлено: 18:42 01-12-2016
Steepe_Hare



Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
LadyOfWood

Цитата:
По мне тут логику менять надо, тут явно лишнии и потоки и ProcessMessages.

 
Это просто крайне упрощенный пример.
Который показывает, что при  
 
 while not Finish do
    Application.ProcessMessages;  
 
Android-приложение зависает, а Windows-программа - нет.
 
Вот я и думаю (независимо от данного примера), какой бы заменитель найти для Application.ProcessMessages под Андроид?
 
 
Добавлено:
Вот другой код, который работает под Виндами, а под Андроидом прога не реагирует на нажатие кнопки Stop:
 
var
  Cancelled: Boolean;
 
implementation
 
{$R *.fmx}
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  StopButton.Enabled := False;
end;
 
procedure TForm1.StartButtonClick(Sender: TObject);
var
  I: Integer;
begin
  StartButton.Enabled := False;
  StopButton.Enabled := True;
  Cancelled := False;
  I := 0;
 
  while not Cancelled do
  begin
    Application.ProcessMessages;
    Inc(I);
    Label1.Text := IntToStr(I);
  end;
 
  StartButton.Enabled := True;
  StopButton.Enabled := False;
end;
 
procedure TForm1.StopButtonClick(Sender: TObject);
begin
  Cancelled := True;
end;

Всего записей: 1162 | Зарегистр. 27-10-2001 | Отправлено: 22:33 01-12-2016
LadyOfWood

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору

Цитата:
Application.ProcessMessages;  

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

Всего записей: 620 | Зарегистр. 16-09-2003 | Отправлено: 00:06 02-12-2016
ShIvADeSt



Moderator
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Steepe_Hare

Цитата:
while not Finish do
    Application.ProcessMessages;

вечный цикл без малейшего слипа, который только и делает что ПОЗВОЛЯЕТ окну обработать какие либо сообщения. По идее должен проц жрать (можешь отмониторить в диспетчере задач загрузку). И как следствие в андроиде приводить к зависанию.
Application.ProcessMessages обычно используется когда выполняется какая либо обработка с прогресс баром, чтобы он отрисовывался или в аналогичных случаях.


----------
И создал Бог женщину... Существо получилось злобное, но забавное...

Всего записей: 3956 | Зарегистр. 29-07-2003 | Отправлено: 02:06 02-12-2016
Steepe_Hare



Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
ShIvADeSt

Цитата:
Application.ProcessMessages обычно используется когда выполняется какая либо обработка с прогресс баром, чтобы он отрисовывался или в аналогичных случаях.  

 
и для того, чтобы прога реагировала на нажатия клавиш

Всего записей: 1162 | Зарегистр. 27-10-2001 | Отправлено: 12:00 02-12-2016
LadyOfWood

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору

Цитата:
и для того, чтобы прога реагировала на нажатия клавиш

Она и без него нормально реагирует, тут проблемы с архитектурой. ProcessMessages это заплатка.

Всего записей: 620 | Зарегистр. 16-09-2003 | Отправлено: 13:06 02-12-2016
Dronton2

Full Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору

Цитата:
и для того, чтобы прога реагировала на нажатия клавиш

Есть универсальное правило: работу, занимающую длительное время - нужно выводить в отдельный поток. Тогда остальные потоки будут работать нормально. В том числе, и пользовательский - реагировать на нажатия кнопок и отрисовывать свои элементы.
 
Добавлено:
Alexzzy

Цитата:
Я напрямую обмениваюсь с использованием критических секций - удобно, надежно...

С критическими секциями работать сложнее, и для них требования жёстче: https://ru.wikipedia.org/wiki/%D0%9A%D1%80%D0%B8%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B0%D1%8F_%D1%81%D0%B5%D0%BA%D1%86%D0%B8%D1%8F
И модель памяти у Delphi - не самая лучшая, насколько я помню.
С сообщениями работать проще. Для "живости" пользовательского интерфейса - самое оно.
Например, отдельный поток делает какую-то длительную работу. В процессе работы, посылает сообщения в основной поток о количестве сделанной работы. В основном потоке, когда он получит доступ к процессору, сообщения читаются и, например, отрисовывается прогресс-бар. Весь UI в Windows и Android построен на обмене сообщениями.

Всего записей: 460 | Зарегистр. 27-06-2005 | Отправлено: 13:12 02-12-2016 | Исправлено: Dronton2, 13:33 02-12-2016
LadyOfWood

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору

Цитата:
С критическими секциями работать сложнее, и для них требования жёстче

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

Всего записей: 620 | Зарегистр. 16-09-2003 | Отправлено: 13:36 02-12-2016
Dronton2

Full Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Если ресурс используется во многих местах программы, то во всех местах, где этот ресурс читается/записывается - нужно не забыть прописать критическую секцию. Если таких ресурсов много - код усложнится пропорционально. Если где-нибудь забудете создать критическую секцию, то иногда, в непредвиденные моменты, появятся неотлавливаемые баги.
И нет у меня доверия к модели памяти Дельфи. Я не смог найти официального документа, описывающего эту модель. Если у вас есть - бросьте ссылку.

Всего записей: 460 | Зарегистр. 27-06-2005 | Отправлено: 14:34 02-12-2016
LadyOfWood

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору

Цитата:
Если ресурс используется во многих местах программы, то во всех местах, где этот ресурс читается/записывается - нужно не забыть прописать критическую секцию.

Логично ресурс обертывается в Enter/Leave.

Цитата:
Если где-нибудь забудете создать критическую секцию

Так никто не мешает это делать например внутри класса, и получаем thread-safe класс.  

Цитата:
И нет у меня доверия к модели памяти Дельфи.  

А что вы вкладываете в это понятие?

Всего записей: 620 | Зарегистр. 16-09-2003 | Отправлено: 16:15 02-12-2016
asutp2

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Dronton2, вот мой простейший пример класса, с которым работа с многопотоковым доступом к свойству класса в Windows/Android/iOS/OSX не вызывает проблем:
 
type
  TDemo = class
  private
    FIntlCritical: System.SyncObjs.TCriticalSection;
    FIntlDemoProperty: Int64;
    function GetDemoProperty: Int64;
    procedure SetDemoProperty(Value: Int64);
  public
    property DemoProperty: Int64 read GetDemoProperty write SetDemoProperty;
    constructor Create;
    destructor Destroy; override;
    procedure Lock;
    procedure UnLock;
  end;
 
function TDemo.GetDemoProperty: Int64;
begin
  Lock;
  try
    Result := FIntlDemoProperty;
  finally
    UnLock;
  end;
end;
 
procedure TDemo.SetDemoProperty(Value: Int64);
begin
  Lock;
  try
    FIntlDemoProperty := Value;
  finally
    UnLock;
  end;
end;
 
constructor TDemo.Create;
begin
  FIntlCritical := System.SyncObjs.TCriticalSection.Create;
end;
 
destructor TDemo.Destroy;  
begin
  FreeAndNil(FIntlCritical);
  inherited Destroy;
end;
 
procedure TDemo.Lock;
begin
  FIntlCritical.Acquire;
end;
 
procedure TDemo.UnLock;
begin
  FIntlCritical.Release;
end;

Всего записей: 790 | Зарегистр. 22-10-2004 | Отправлено: 16:19 02-12-2016
Dronton2

Full Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору

Цитата:
модели памяти Дельфи

https://en.wikipedia.org/wiki/Memory_model_(programming)
Есть врезка на русском: Модель памяти
Есть частное мнение про модель памяти для Дельфи: http://stackoverflow.com/questions/12917512/delphi-concurrency-memory-model
Но там, как-то не очень весело выходит:
Цитата:
The compiler codegen patterns rely on the x86 architecture rules that register-sized writes to global memory at aligned addresses are atomic. Writing of large data, byte data, or at unaligned addresses may cross a cache line and require two separate write operations within the single write instruction. The Delphi compiler is (mostly) oblivious to this.

И совершенно очаровательная статья, показывающая, какие проблемы нужно учитывать в многопоточных приложениях: https://habrahabr.ru/post/310130/ (хоть на примере Явы, но актуально для любых языков)

Всего записей: 460 | Зарегистр. 27-06-2005 | Отправлено: 16:41 02-12-2016
asutp2

Advanced Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Dronton2, в делфи мы не используем указанный в статье на хабре новый уровень абстракции, т.к. у нас генерится нативный код

Всего записей: 790 | Зарегистр. 22-10-2004 | Отправлено: 16:51 02-12-2016
Открыть новую тему     Написать ответ в эту тему

Страницы: 1 2

Компьютерный форум Ru.Board » Компьютеры » Прикладное программирование » Зависание программы и TThread


Реклама на форуме Ru.Board.

Powered by Ikonboard "v2.1.7b" © 2000 Ikonboard.com
Modified by Ru.B0ard
© Ru.B0ard 2000-2024

BitCoin: 1NGG1chHtUvrtEqjeerQCKDMUi6S6CG4iC

Рейтинг.ru