2017-02-09 4 views
1

Я знаю, что потоки Delphi обсуждались на многих потоках. Я попробовал пересмотреть их, но не нашел ответа на мой вопрос.Правильно ли этот код в Delphi Thread?

фона: Я обнаружил, что освобождая TWebBrowser может занять 10+ второй после того, как браузер загружен Adobe Acrobat Reader DC. Я думаю, что как-то это проверяет наличие обновлений или что-то в этом роде. Это раздражает при попытке закрыть форму с помощью браузера.

Я подумал, что, возможно, у меня может быть фоновый поток, выпущенный браузером. Поэтому я переместил переменную браузера в глобальную переменную (конфиденциально сохраненную в части реализации модуля). Только одна из этих форм будет использоваться одновременно. Затем я попытался освободить поток в фоновом режиме. Это не работает, как я ожидал.

Пример кода

interface 
    TMyform = class(TForm) 
    pnlBowserHolder: TPanel; 
    procedure FormDestroy(Sender: TObject); 
    procedure FormCreate(Sender: TObject); 
    private 
    //WebBrowser : TWebBrowser; <-- moved to global variable 
    public 
    { Public declarations } 
    end; 

implementation 

type 
    TBackgroundBrowserKillerThread = class(TThread) 
    public 
    procedure Execute; override; 
    end; 

var 
    WebBrowser : TWebBrowser; 
    BrowserKillerThread : TBackgroundBrowserKillerThread; 

procedure TfrmLabImageViewer.FormCreate(Sender: TObject); 
begin 
    WebBrowser := TWebBrowser.Create(Self); 
    TWinControl(WebBrowser).Parent := pnlBowserHolder; 
    WebBrowser.Align := alClient; 
end; 

procedure TfrmLabImageViewer.FormDestroy(Sender: TObject); 

begin 
    BrowserKillerThread := TBackgroundBrowserKillerThread.Create(true); 
    Application.ProcessMessages; 
    BrowserKillerThread.Execute(); 
    //WebBrowser.Free; 
end; 

procedure TBackgroundBrowserKillerThread.Execute(); 
begin 
    TWinControl(WebBrowser).Parent := nil; 
    FreeAndNil(WebBrowser); 
    self.FreeOnTerminate := true; 
    BrowserKillerThread := nil; //free reference to thread, shouldn't affect ability of self to free itself (?) 
end; 

Вопросы:

  • Когда я пошагово код FormDestroy в режиме отладки, то строка, содержащая BrowserKillerThread.Execute(); требуется еще 10 секунд. Я думал, что это запустит другой поток и немедленно вернется. Но это не так. Разве мое понимание ошибочно в отношении того, что делает .execute? Или что-то смешное происходит?
  • Является ли что-то плохое? Я прочитал, что VCL не является потокобезопасным, и что он не может/не должен иметь доступ к объектам VCL из другого потока. Я надеялся, что это не будет применяться в этом случае, так как я просто освобождаю объект без дальнейшего взаимодействия.
  • Если нить свободна сама по завершении, я предполагаю, что это приведет к тому, что мой указатель BrowserKillerThread свисает. Так нормально ли назначать ноль этому, как я?
  • Любые предложения о том, как это сделать лучше?

Большое спасибо.

KT

+3

Это никогда не сработает. Вы не можете получить доступ к элементу управления VCL любого вида из любого места, кроме основного потока, и вы не можете создать его в одном потоке и уничтожить его в другом. Код, который вы написали, ничего не меняет - браузер по-прежнему уничтожается основным потоком, которому он принадлежит; вы просто вызываете этот код из своего потока (что неправильно, если вы не делаете это через Synchronize). –

+4

Дополнительно ... Возможно, вы захотите снова просмотреть все эти темы. Бьюсь об заклад, вы не видели ни одного примера, вызывающего метод execute извне. Зачем? Потому что тогда код в методе выполнения будет выполняться в контексте потока, который его вызывает. –

+2

Я не получаю задержку при закрытии приложения, которое вы описываете, используя TWebBrower + v.15.023 Acro Reader DC. Можете ли вы привести пример URL-адреса, который вызывает задержку для вас? – MartynA

ответ

8

Это мое понимание того, что неправильно .Execute делает ??

Да. Это не то, как вы делаете потоки. Как правило, сначала вам нужно решить, нужно ли вам что-либо делать с потоком после того, как вы его начали сначала. Если да, сохраните ссылку, не используйте FreeOnTerminate и позаботьтесь об уничтожении нити самостоятельно после ее прекращения. Если вы этого не сделаете, не держите ссылку, установите FreeOnTerminate и отправьте ее.

Вы не должны устанавливать FreeOnTerminate в рамках выполнения потока, а также не сохранять глобальную ссылку на поток, который имеет этот набор. Вы также не запускаете нить, вызывая метод Execute, но создавая его без приостановки или вызывая Start. Просто вызов Execute фактически не запускает поток, но выполняет эту процедуру в контексте текущего потока, поэтому он все равно занимает 10 секунд.

Ваш пример станет:

procedure TfrmLabImageViewer.FormDestroy(Sender: TObject); 
    var BrowserKillerThread: TBackgroundBrowserKillerThread; 
begin 
    BrowserKillerThread := TBackgroundBrowserKillerThread.Create(true); 
    BrowserKillerThread.FreeOnTerminate := true; 
    BrowserKillerThread.Start; 
end; 

procedure TBackgroundBrowserKillerThread.Execute(); 
begin 
    TWinControl(WebBrowser).Parent := nil; // I don't think you need to nil the parent here, but probably does no harm 
    FreeAndNil(WebBrowser); 
end; 

Теперь потоковая правильно, но логика по-прежнему не так.Как вы уже заметили, вы не должны изменять объекты VCL из потоков, отличных от основного потока, это включает Free. Вам нужно будет сделать это с помощью Synchronize, что приведет к поражению цели резьбы.

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

+0

Отличный и полезный комментарий. Спасибо! – kdtop

+0

Я немного поиграл с этим. Я использую Delphi 10. Он выглядит как BrowserKillthread.Start вместо этого должен быть BrowserKillthread.Resume. В моей версии Delphi нет .Start. Еще раз спасибо ... – kdtop

+0

Я решил следовать совету DNR, особенно с MartynA, заявив, что он не испытывал задержки с закрытием и пошел по основной причине. Я открыл приложение Acrobat из меню «Пуск» Windows и перешел в настройки редактирования. Я отключил все настройки, которые мог найти, ссылаясь на разговоры с серверами безопасности и т. Д. И т. Д., И сделал исключение безопасности для папки, в которую загружался мой .pdf (только просмотр локальных файлов) ... И, конечно же, Задержка в 10 секунд исчезла. Я пишу это здесь, если у кого-то другие проблемы. Еще раз спасибо за помощь. – kdtop

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