Delphi多线程编程中的技巧

http://sytlm.ycool.com/post.2736328.html

(1)创建线程
MsgThread := TMsgThread.Create(False) ; //创建并执行线程
MsgThread := TMsgThread.Create(True) ; //创建线程后挂起
constructor Create(CreateSuspended: Boolean); 中的参数CreateSuspended表示创建后是否挂起线程。
(2)设置线程里没有设置循环执行的话,且设置FreeOnTerminate为True,则线程执行完后就会自己释放。
(3)在一个线程结束后,调用另一个事件的方法:
只要设置Onterminate:=某方法,这样在线程结束前自然会被调用,比如 :
procedure TSendShortMessageThread.Execute;
var
Bitmap: Tbitamp;
begin
Bimap:=Tbitmap.create(nil) ;
OnTerminate:=Threaddone;
end;

procedure Threaddone(sender: tobject);
begin
Bimap.Free; //在Destory之前会被调用
end;
(4)程序结束前安全的退出线程的方法:
if MsgThread <> nil then
begin
MsgThread.Terminate ;
MsgThread.WaitFor ;
end;
(5)判断当前线程的状态:
//以下资料来自大富翁论坛。
/判断线程是否释放
//返回值:0-已释放;1-正在运行;2-已终止但未释放;
//3-未建立或不存在
function TFrmMain.CheckThreadFreed(aThread: TThread): Byte;
var
i: DWord;
IsQuit: Boolean;
begin
if Assigned(aThread) then
begin
IsQuit := GetExitCodeThread(aThread.Handle, i);
if IsQuit then //If the function succeeds, the return value is nonzero.
//If the function fails, the return value is zero.
begin
if i = STILL_ACTIVE then //If the specified thread has not terminated,
//the termination status returned is STILL_ACTIVE.
Result := 1
else
Result := 2; //aThread未Free,因为Tthread.Destroy中有执行语句
end
else
Result := 0; //可以用GetLastError取得错误代码
end
else
Result := 3;
end;
(6)线程同步。
如果线程要调用VCL里面的内容(如:别的窗体中的控件),就需要将这个线程同步。线程同步表示交由主线程运行这段代码,各个线程都在主线程中分时间段运行。另外,要想避免多个线程同时执行同一段代码也需要将多线程同步。
临界区和互斥的作用类似,都是用来进行同步的,但它们间有以下一点差别:
临界区只能在进程内使用,也就是说只能是进程内的线程间的同步;而互斥则还可用在进程之间的;临界区所花消的时间很少,才10~15个时间片,而互斥需要400多个;临界区随着进程的终止而终止,而互斥,如果你不用closehandle()的话,在进程终止后仍然在系统内存在,也就是说它是系统全局对象;
同步的方法有:

(1)使用临界区对象。
临界区对象有两种:TRTLCriticalSection 和 CriticalSection。
 TRTLCriticalSection的用法

var
GlobalVariable:Double;

var
CriticalSection:TRTLCriticalSection;

procedure SetGlobalVariable(Value:Double);
begin
EnterCriticalSection(CriticalSection); //进入临界区
try
GlobalVariable:=Value;
finally
LeaveCriticalSection(CriticalSection); //离开临界区
end;
end;

initialization
InitializeCriticalSection(CriticalSection); //初始化
finalization
DeleteCriticalSection(CriticalSection); //删除
end.
 CriticalSection(重要区段)的用法:
var criticalsection: TCriticalsection;
创建:criticalsection := TCriticalsection.create;
使用:
criticalsection.enter;
try
...
finally
criticalsection.leave;
end;

(2)使用互斥
先在主线程中创建事件对象:
var
hMutex: THandle = 0;
...
hMutex := CreateMutex(nil, False, nil);

在线程的Execute方法中加入以下代码:
if WaitForSingleObject(hMutex, INFINITE) = WAIT_OBJECT_0 then
//Do Something;
...
ReleaseMutex(hMutex);

最后记得要释放互斥对象:
CloseHandle(hMutex);

(3)使用信号量

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls;

type
TForm1 = class(TForm)
Edit1: TEdit;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

type
TMyThread = class(TThread)
private

protected
procedure Execute; override;
public

constructor Create; virtual;
end;

var
Form1 : TForm1;
HSem : THandle = 0 ;
implementation

{$R *.dfm}

var
tick: Integer = 0;
procedure TMyThread.Execute;
var
WaitReturn : DWord ;
begin
WaitReturn := WaitForSingleObject(HSem,INFINITE) ;
Form1.Edit1.Text := IntToStr(tick);
Inc(tick);
Sleep(10);
ReleaseSemaphore(HSem, 1, Nil)
end;

constructor TMyThread.Create;
begin
inherited Create(False);
FreeOnTerminate := True;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
HSem := CreateSemaphore(Nil,1,1,Nil) ;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
CloseHandle(HSem) ;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
index: Integer;
begin
for index := 0 to 10 do
begin
TMyThread.Create;
end;
end;

end.
一般的同步对象使用Mutex对象,是因为Mutex有一个特别之处:当一个持有对象的线程DOWN掉的时候,mutex对象可以自动让其它等待这个对象的线程接受,而其它的内核对象则不具体这个功能。
之所要使用Semaphore则是因为Semaphore可以提供一个活动线程的上限,即lMaximumCount参数,这才是它的真正有用之处。


Delphi 可以快速开发桌面程序,用来做dll 封装操作,封装窗体都是很方便的。
在 delphi 做动态库时,会自动提示要 uses ShareMem,这个实际用起来是不方便的,因为 dll 可能要发布,要给其他人用,而别人用什么语言来开发是说不准的,如果不是delphi,就没办法用了。因此在接口上一般是用 pchar来代替string。但是在内部,string 还是可以拿来用的。

这样就会产生一个问题,如果动态库是支持多线程来用的,而在动态库内部并没有显式的创建一个线程时,烦繁使用 string 就说不定什么时候会产生一个内存出错。

 

delphi isMultiThread                 

Indicates whether the application spawned additional threads using BeginThread or TThread objects.

IsMultiThread is set to True to indicate that the memory manager should support multiple threads. IsMultiThread is set to True by BeginThread and class factories.

 

在Delphi 内部,定义了一个 isMultiThread 的boolean 型全局变量(好象是在 system 单元)。默认这个变量是 false的。当显示地创建一个线程时(如TThread.Create),会置成true。否则一直是false。
这个变量唯一使用的地方是在 GetMem 和 FreeMem时,如果为 True,会先进入临界区,操作完成后退出。如果为false就没有临界区了。

因此在编写多线程安全的动态库时,一定要记得在动态库初始化的时候手动加上 isMultiThread=true; 这样强制delphi来使用临界区操作。

如果调用程序也是用 delphi 来写的,可能会多线程使用,那最好也在初始化的时候加上这句。值得注意的是,调用程序和动态库的这两个变量是两个互不相干的,也就是两边都得加,只在一方加上是影响不了另一方的。

其他有关多线程安全的就是一些常识了,对可能多线程访问的代码,要用事件,信号量,临界区等方法加以保护。使用方法可以参考delphi的帮助文档,或msdn,或网上的大堆文章。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值