Приложение очень долго синхронизирует потоки (или вообще не синхронизирует)

Я использую компоненты REST в Delphi XE5 (iOS и Android). В настоящее время я тестирую с помощью симулятора iOS, и мое приложение часто зависает на следующей строке кода:

R_Request.Execute;

После некоторой отладки я обнаружил, что он конкретно зависает в этой строке кода в REST.Client.PAS:

HandleEvent(DoAfterExecute);

который выглядит так:

procedure TCustomRESTRequest.HandleEvent(AEventHandler: TMethod);
begin
  // Handle Synchronized if we are NOT already in the main thread
  // NEVER call synchronize on the MainThread - that might shift the island!
  if SynchronizedEvents and (System.MainThreadID <> TThread.CurrentThread.ThreadID) then
    TThread.Synchronize(TThread.CurrentThread, AEventHandler) // FAILS HERE
  else
    AEventHandler;
end;

Это либо НЕ возвращает поток в .Synchronize, либо занимает очень много времени (5 минут или около того)... Он работал один раз во время отладки, но с тех пор не работает и снова только сейчас (30 мин. позже и после того, как он будет установлен в течение 5 минут, чтобы вернуть поток).

Помощь ? Или, по крайней мере, любое чувство направления, которое кто-нибудь может дать мне?

Опять же, разработка для iOS и Android (так что FMX...) и в настоящее время тестирование с помощью симулятора iOS. Спасибо !


person ThisGuy    schedule 14.04.2014    source источник


Ответы (1)


Проблема в том, что TThread.Synchronize() не работает в FireMonkey. См. этот отчет контроля качества:

Отчет №123579: TThread.Synchronize() и TThread.Queue() работают неправильно в FireMonkey

Это было обнаружено совсем недавно, но оно было взломано с тех пор, как FireMonkey был впервые представлен, и до сих пор никто этого не замечал.

Пока Embarcadero не исправит это, попробуйте периодически вызывать CheckSynchronize() в основном потоке, например, в таймере.

person Remy Lebeau    schedule 14.04.2014
comment
Да, я был шокирован, когда нашел его, и нет хорошего способа вручную реализовать WakeMainThread() для пробуждения основного потока кросс-платформенным образом, потому что разные платформы по-разному обрабатывают очереди событий основного потока. По крайней мере, CheckSynchronize() сам по себе является кроссплатформенным, но заставить RTL вызывать его — это PITA, если только вы сами его не вызовете. - person Remy Lebeau; 15.04.2014
comment
Вомп! Спасибо за CheckSynch. предположение. - person ThisGuy; 15.04.2014
comment
@ Ганс, это действительно решено? Потому что, когда я пытаюсь сделать в области TThread.Execute это self.Synchronize(procedure begin BusyForm.Show end); на android, он возвращает ужасное исключение CalledFromWrongThreadException: Только исходный поток, создавший иерархию представлений, может касаться своих представлений, что предлагается для решения в Android Studio с запуском обновлений пользовательского интерфейса с помощью метода runOnUiThread. Это означает, что он все еще сломан в XE8. Любые замечания приветствуются. - person notricky; 21.01.2017
comment
@notricky, это совсем другая проблема. Сам Synchronize() теперь исправлен, поэтому он выполняет функцию пользователя. Вы столкнулись с новой проблемой, связанной с тем, что основной поток FMX и поток пользовательского интерфейса Android не всегда являются одним и тем же потоком! Это планируется исправить в следующей версии Delphi. А пока в классе FMX.Platform.Android.TPlatformAndroid есть методы для выполнения функций через runOnUIThread(). - person Remy Lebeau; 21.01.2017
comment
@RemyLebeau, спасибо за совет. Если вы не против дать немного больше деталей - значит ли это, что лучшим решением является {IFDEF} весь класс Threaded для ANDROID и остальных платформ? Или вы имеете в виду, что он заменяет только Synchronize вызовов в коде на runInUIThread() один? - person notricky; 22.01.2017
comment
@notricky только последнее, и только для синхронизированного кода, который обязательно должен работать в реальном потоке пользовательского интерфейса, а не просто синхронизироваться вообще. В противном случае рассмотрите возможность изменения кода потока, чтобы вообще не касаться пользовательского интерфейса напрямую. - person Remy Lebeau; 22.01.2017
comment
@RemyLebeau, это вдохновляет. Я хочу иметь настраиваемое наложение (BusyBox), которое будет отображаться всякий раз, когда возникает фоновая многопоточная задача. Сначала я думал, что BusyBox можно показывать из потока задач, но теперь я думаю, что более эффективно иметь отдельный поток BusyBox, который бы управлял платформенными -sync для ящика и будет получать уведомления от потоков задач для этого. Есть ли какие-либо другие проблемы, которые можно решить с помощью этого плана, например, на других платформах, таких как MACOS, IOS? - person notricky; 22.01.2017
comment
@notricky на каждой платформе управление пользовательским интерфейсом всегда должно быть отделено от управления задачами. Только основной поток пользовательского интерфейса должен управлять пользовательским интерфейсом, а рабочие потоки должны управлять только своими фоновыми задачами. Используйте межпотоковое взаимодействие для перехода туда и обратно по мере необходимости. - person Remy Lebeau; 22.01.2017
comment
@RemyLebeau, так вы действительно предлагаете добавить дополнительное сообщение, которое будет отвечать на wm_showbusy в потоке приложения вызовом специального метода Android, или оно просто подходит и будет работать на основной платформе. нить? Или отдельная ветка для этой спецзадачи тоже подойдет? - person notricky; 22.01.2017
comment
@RemyLebeau, моя проблема решена довольно просто. Что касается TPlatformAndroid, который спрятан в разделе реализации модуля, но наводит на некоторые размышления (полагаю, что проблему можно решить на кросс-платформенной основе), я нашел хороший процесс CallInUIThreadAndWaitFinishing внутри FMX.Android.Helpers, который делает свое дело. Так что мне нужно только {$IFDEF}. Как всегда, я открыт для замечаний. - person notricky; 23.01.2017
comment
Извините, я не могу отредактировать свой последний пост, но правильный путь для XE8 - FMX.Helpers.Android - person notricky; 24.01.2017