这个问题似乎微不足道,但我希望你不要忽视它.
在销毁TThread对象之前,通常需要等到调用TThread.Execute()方法的线程完成,因为只有这样我们才能确定,例如,不再访问在类的析构函数中销毁的对象.因此,有必要调用Terminate来设置线程必须检查的Terminated标志以知道是否退出,然后调用WaitFor()方法.
因为线程可能被挂起,我认为在调用WaitFor之前恢复它是好的,否则调用线程将会死锁.并且因为线程可以多次挂起,所以应该恢复相同的次数,对吗?
while Suspended do
Resume;
Run Code Online (Sandbox Code Playgroud)
如果线程被创建为暂停,我们不必担心当我们恢复线程只会终止它时将调用TThread.Execute()方法 - 它不会(如果我错了请纠正我).
我所说的建议为每个被释放的TThread对象使用以下代码行:
MyThread.Terminate;
while MyThread.Suspended do
MyThread.Resume;
MyThread.WaitFor;
MyThread.Free;
Run Code Online (Sandbox Code Playgroud)
不幸的是,当我们销毁创建多个线程的应用程序时,为每个被破坏的TThread对象编写这样一段代码会使代码变得非常长,甚至可能不透明.
因此我得出结论,所有这些都可以放在TThread类的重写析构函数中,这要归功于调用MyThread.Free(如果设置了MyThread.FreeOnTerminate,则调用MyThread.Terminate),而不关心是否销毁object是否是TThread对象:
destructor TMyThread.Destroy;
begin
//if FreeOnTerminate, the calling thread cannot wait for itself
if GetCurrentThreadId <> ThreadId then
begin
Terminate;
while Suspended do
Resume;
WaitFor;
end;
{free all objects created in this class}
inherited Destroy;
end;
Run Code Online (Sandbox Code Playgroud)
请原谅我提出这样一个基本问题.但是,我希望通过这种方式了解您的意见 - 我希望这是一种普遍的方式 - 销毁TThread对象.我问这个问题,因为我从我的同事的代码中学到了他们通常使用第一个代码示例来销毁这些对象,但他们从来没有用来检查等待的线程是否没有被挂起,如果线程我认为有点危险可能会在代码中的某处暂停.因此,我试图找到一种破坏这个类的对象的通用方法,这将使代码更清晰,更安全.我希望我没有让情况变得更糟 - 你怎么看?
提前感谢您的建议.
之前有人问过,但没有完整答案.这与所谓的着名"'致命线程模型!'"有关.
我需要用安全的东西替换这个调用TThread.Suspend,当终止或恢复时返回:
procedure TMyThread.Execute;
begin
while (not Terminated) do begin
if PendingOffline then begin
PendingOffline := false; // flag off.
ReleaseResources;
Self.Suspend; // suspend thread. { evil! ask Barry Kelly why.}
// -- somewhere else, after a long time, a user clicks
// a resume button, and the thread resumes: --
if Terminated then
exit; // leave TThread.Execute.
// Not terminated, so we continue..
GrabResources;
end;
end;
end;
Run Code Online (Sandbox Code Playgroud)
最初的答案含糊地暗示了"TMutex,TEvent和关键部分".
我想我正在寻找一个TThreadThatDoesntSuck.
以下是带有Win32Event的TThread派生示例,供评论:
unit SignalThreadUnit;
interface
uses
Classes,SysUtils,Windows;
type
TSignalThread …Run Code Online (Sandbox Code Playgroud) 很久以前,当我开始使用Delphi中的线程时,我通过TThread.Resume在构造函数的末尾调用来创建线程,并且仍然如此:
constructor TMyThread.Create(const ASomeParam: String);
begin
inherited Create(True);
try
FSomeParam:= ASomeParam;
//Initialize some stuff here...
finally
Resume;
end;
end;
Run Code Online (Sandbox Code Playgroud)
从那时起,Resume一直被弃用Start而不是使用.但是,Start只能从线程外部调用,并且不能从构造函数内部调用.
我继续使用Resume如上所示设计我的线程,虽然我知道它已被弃用 - 只是因为我不想Start从线程外部调用.我发现要打电话有点乱:
FMyThread := TMyThread.Create(SomeParamValue);
FMyThread.Start;
Run Code Online (Sandbox Code Playgroud)
问题:这个改变的原因是什么?我的意思是,使用Resume他们希望我们使用它的错误是什么Start?
编辑在Sedat的回答之后,我想这实际上取决于在构造函数中,线程实际开始执行的时间.
我想在后台运行一个线程,它将以给定的时间间隔检查与某个服务器的连接.例如每5秒.
我不知道这是否有一个好的"设计模式"?如果我记得正确的话,我已经读过一些在其执行方法中睡眠线程不好的地方.但我可能错了.
另外,我可以使用普通的TThread类或OTL线程库.
有任何想法吗?
谢谢.
当FreeOnTerminate = True时,为TThread后代编写Delphi DUnit测试的最佳方法是什么?TThread后代返回一个我需要测试的引用,但我无法弄清楚如何等待线程在测试中完成...
unit uThreadTests;
interface
uses
Classes, TestFramework;
type
TMyThread = class(TThread)
strict private
FId: Integer;
protected
procedure Execute; override;
public
constructor Create(AId: Integer);
property Id: Integer read FId;
end;
TestTMyThread = class(TTestCase)
strict private
FMyId: Integer;
procedure OnThreadTerminate(Sender: TObject);
protected
procedure SetUp; override;
procedure TearDown; override;
published
procedure TestMyThread;
end;
implementation
{ TMyThread }
constructor TMyThread.Create(AId: Integer);
begin
FreeOnTerminate := True;
FId := AId;
inherited Create(False);
end;
procedure TMyThread.Execute;
begin
inherited;
FId := FId + 1;
end; …Run Code Online (Sandbox Code Playgroud) 在Delphi 2009中,我发现无论何时在应用程序中使用TThread.CurrentThread,我都会在应用程序关闭时收到如下错误消息:
Exception EAccessViolation in module ntdll.dll at 0003DBBA.
Access violation at address 7799DBBA in module 'ntdll.dll'. Write of
address 00000014.
Run Code Online (Sandbox Code Playgroud)
除非它只是我的机器,你可以在几秒钟内复制它:创建一个新的Delphi Forms Application,在表单中添加一个按钮,并使用类似下面的按钮的事件处理程序:
procedure TForm1.Button1Click(Sender: TObject);
begin
TThread.CurrentThread;
end;
Run Code Online (Sandbox Code Playgroud)
在我的Vista的机器和我的XP的机器都我发现,如果我不按一下按钮一切都很好,但如果我做的点击按钮,我得到上面的错误消息,当我关闭应用程序.
所以...我想知道这是不是一个错误,但与此同时我认为我很可能根本不理解你应该如何在Delphi中使用TThreads.我有点像德尔福新手,我很害怕.
使用TThread.CurrentThread有什么明显错误吗?
如果没有,并且你有Delphi 2009,如果你实现我的简单示例项目,你会遇到同样的问题吗?
我有一个有60台计算机/设备的房间(40台计算机和20台基于Windows CE的示波器),我想知道哪个和每个人都在使用ping.首先我写了一个标准的ping(请参阅此处Delphi Indy Ping错误10040),现在工作正常,但大多数计算机脱机时需要很长时间.
所以我要做的就是编写一个MultiThread Ping,但我很挣扎.我在互联网上看到的例子很少,没有人能满足我的需求,这就是为什么我自己尝试写这个例子.
我使用XE2和Indy 10,表单只包含备忘录和按钮.
unit Main;
interface
uses
Winapi.Windows, System.SysUtils, System.Classes, Vcl.Forms,
IdIcmpClient, IdGlobal, Vcl.StdCtrls, Vcl.Controls;
type
TMainForm = class(TForm)
Memo1: TMemo;
ButtonStartPing: TButton;
procedure ButtonStartPingClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
type
TMyPingThread = class(TThread)
private
fIndex : integer;
fIdIcmpClient: TIdIcmpClient;
procedure doOnPingReply;
protected
procedure Execute; override;
public
constructor Create(index: integer);
end;
var
MainForm: TMainForm;
ThreadCOunt : integer;
implementation
{$R *.dfm}
constructor TMyPingThread.Create(index: integer);
begin
inherited Create(false); …Run Code Online (Sandbox Code Playgroud) 我正在使用LoadKeyboardLayout函数以这种方式加载和激活键盘布局:
procedure TfrmMain.eSearchEnter(Sender: TObject);
begin
LoadKeyboardLayout('00000429', KLF_ACTIVATE);
end;
Run Code Online (Sandbox Code Playgroud)
它工作得很好,但它冻结活动形式1-2秒,因为这种变化需要一些时间.为了防止这种情况,我将此代码移动到这样的后台线程:
type
FLangChangeThread = class(TThread)
private
FLang: string;
protected
procedure Execute; override;
public
property Lang: string read FLang write FLang;
end;
implementation
procedure FLangChangeThread.Execute;
begin
if FLang = 'EN' then
LoadKeyboardLayout('00000409', KLF_ACTIVATE)
else
if FLang = 'FA' then
LoadKeyboardLayout('00000429', KLF_ACTIVATE);
end;
Run Code Online (Sandbox Code Playgroud)
这个后台线程我正在以这种方式运行:
procedure TfrmMain.ChangeWritingLanguage(ALang: string);
begin
with FLangChangeThread.Create(True) do
begin
FreeOnTerminate := True;
Lang := ALang;
Resume;
end;
end;
procedure TfrmMain.eSearchEnter(Sender: TObject);
begin
ChangeWritingLanguage('FA');
end;
Run Code Online (Sandbox Code Playgroud)
问题是,它没有按预期更改键盘布局.我调试了代码并且所有行都被执行了; 只是LoadKeyboardLayout函数没有完成它的工作.
如何从后台线程使 …
我的程序中有一个主线程和一个单独的线程.如果单独的线程在主线程之前完成,它应该自动释放.如果主线程首先完成,它应该释放单独的线程.
我知道FreeOnTerminate,我读过你必须小心使用它.
我的问题是,以下代码是否正确?
procedure TMyThread.Execute;
begin
... Do some processing
Synchronize(ThreadFinished);
if Terminated then exit;
FreeOnTerminate := true;
end;
procedure TMyThread.ThreadFinished;
begin
MainForm.MyThreadReady := true;
end;
procedure TMainForm.Create;
begin
MyThreadReady := false;
MyThread := TMyThread.Create(false);
end;
procedure TMainForm.Close;
begin
if not MyThreadReady then
begin
MyThread.Terminate;
MyThread.WaitFor;
MyThread.Free;
end;
end;
Run Code Online (Sandbox Code Playgroud) 我刚刚意识到我的异常没有在我的线程中向用户显示!
起初我在我的线程中使用它来引发异常,这不起作用:
except on E:Exception do
begin
raise Exception.Create('Error: ' + E.Message);
end;
Run Code Online (Sandbox Code Playgroud)
IDE向我显示了异常,但我的应用程序没有!
我四处寻找解决方案,这就是我发现的:
http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_22039681.html
这些都不适合我.
这是我的Thread单元:
unit uCheckForUpdateThread;
interface
uses
Windows, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
IdHTTP, GlobalFuncs, Classes, HtmlExtractor, SysUtils, Forms;
type
TUpdaterThread = class(TThread)
private
FileGrabber : THtmlExtractor;
HTTP : TIdHttp;
AppMajor,
AppMinor,
AppRelease : Integer;
UpdateText : string;
VersionStr : string;
ExceptionText : string;
FException: Exception;
procedure DoHandleException;
procedure SyncUpdateLbl;
procedure SyncFinalize;
public
constructor Create;
protected
procedure HandleException; virtual;
procedure Execute; override;
end;
implementation
uses …Run Code Online (Sandbox Code Playgroud) delphi ×10
tthread ×10
delphi-2009 ×2
delphi-2010 ×1
delphi-7 ×1
deprecated ×1
destructor ×1
dunit ×1
exception ×1
indy ×1
ping ×1
raise ×1
unit-testing ×1