在线程中,Synchronize和用消息处理哪个效率高?(200分)

  • 主题发起人 主题发起人 教父
  • 开始时间 开始时间

教父

Unregistered / Unconfirmed
GUEST, unregistred user!
我们知道,大部分的VCL是线程不安全的,所以在用线程操作VCL的时候必须要用到Synchronize。
我们再来看看Synchronize是个什么东西。
《Delphi4编程技术内幕》的P126中说:“具体来说,Synchronize做一些控制工作,使你的线
程临时成为应用程序主线程的一部分。在这个进程中,你可以访问VCL。当不再需要VCL的时候,
你应当为断代码中的同步部分。于是你的程序将再次具有多个线程。”
言下之意,当某个线程访问VCL的时候,所有的其它线程都停止下来了,我想这是因为其它的
线程要等待访问VCL。
《Delphi4开发大全(上)》的P427中揭示了Synchronize的奥秘:“当您创建一个线程对象时,
VCL会创建和维护一个隐含的线程窗口。这个窗口的作用是把通过Synchronize()调用的方法
排队。Synchronize把通过Method参数传递过来的方法保存在TThread的FMethod字段中,然后
把一个CM_EXECPROC消息发给线程窗口,并且把消息的lParam参数设为Self(这里就是线程对
象)。当线程窗口的窗口程序收到这个消息,它就调用FMethod字段所指定的方法。由于线程
窗口是在主线程内创建的,线程窗口的窗口过程也被主线程执行。因此,FMethod字段所指定
的方法就在主线程内执行。”
《C++Builder5开发人员指南》中同样有类似的描述:“Synchronize方法等待主VCL线程进入
消息循环,然后执行传入的方法。”
从上面引述的观点来看,我们可以清楚地知道如果一个线程要操作VCL,它就必须得停下来,
等待其它的线程让出VCL,这样,线程的效率自然就降低了。
在《Delphi4开发大全》中也提到了可以通过消息来进行线程的同步,于是我编写了如下的代
码。
//下面是线程单元
unit Unit2;
interface
uses
SysUtils,Classes,Unit1,Windows;
type
TMsgThread = class(TThread)
private
{ Private declarations }
FStr:String;
procedure ShowInMemo;
protected
procedure Execute;
override;
public
Num:Integer;
end;

implementation
{$DEFINE USE_MSG} //这一句决定是采用消息还是采用Synchronize。
procedure TMsgThread.Execute;
begin
while not Terminateddo
begin
FStr:='第'+IntToStr(Num)+'行:'+IntToStr(GetTickCount);
{$IFDEF USE_MSG}
PostMessage(Form1.Handle,WM_MSG,Num,Integer(@FStr[1]));
{$else
}
Synchronize(ShowInMemo);
{$ENDIF}
end;
end;

procedure TMsgThread.ShowInMemo;
begin
Form1.Memo1.Lines.Strings[Num-1]:=FStr;
end;

end.

//以下是主程序
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
const
WM_MSG=WM_USER+1;
type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
protected
procedure WMMsg(var Msg:TMessage);message WM_MSG;
end;

var
Form1: TForm1;
implementation
uses Unit2;
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
i:integer;
begin
for i:=1 to 10do
begin
Memo1.Lines.Add('');
with TMsgThread.Create(True)do
begin
Num:=i;
Resume;
end;
end;
end;

procedure TForm1.WMMsg(var Msg: TMessage);
var
i:integer;
s:String;
begin
i:=Msg.wParam;
s:=String(Pointer(Msg.LParam));
Memo1.Lines.Strings[i-1]:=s;
end;

end.

这个程序其实很简单,建立一些线程,同时操作Memo1,在其中显示字符串。
当在Unit2中定义了USE_MSG时,程序将使用自定义的WM_MSG消息的方式来同步线程,否则所用
Synchronize。
在Unit1中,每当收到WM_MSG时根据MSG的信息来决定写入Memo1的位置和字符串。
两种方式都能正常的工作,但是,我想知道的是:两者的效率孰高孰低?
个人认为,采用消息的方式,线程不用停下来等待VCL的操作权,如果这个线程是个计算
密集型的,这样做无疑会大大提高效率。
当然,对于VCL的访问,此处仍然是逐一处理的。
请各位高手发表看法,谢谢!
 
我对大侠的敬仰有如涛涛江水连绵不绝,又有如.....
只能帮你提前了。
 
Synchronize和用消息没有什么本质区别的吗。
 
赶快收藏,收藏![:D][:D][:D]
 
to liguang:Synchronize也是通过消息来实现,所以说起来应该没有什么本质区别。
但是Synchronize是在线程中处理,个人认为会影响线程的效率,你认为如何?
 
delphi5所有控件全部从新写过,尽量适合多线程。所以delphi5是个里程碑
Synchronize是线程排队,采用消息还是需要消息排队,
如果在消息当中处理耗时的过程,采用多个线程密集发送消息,会不会漏掉消息
 
本人私下已经和教父讨论过一番,不过本人的意见与教父有点相左。本人的意思是用线程
向主程序发送消息,由消息来驱动。实际上,正如教父上面所写的如下过程才是线程所在
完成的真正的功能,而这段代码又在主程序中。
procedure TForm1.WMMsg(var Msg: TMessage);
var
i:integer;
s:String;
begin
i:=Msg.wParam;
s:=String(Pointer(Msg.LParam));
Memo1.Lines.Strings[i-1]:=s;
end;
当然这段代码本身没有问题,假如这段代码是一个死循环或者是一个长达三十分的处理时间
,那就是麻烦了,窗体不能再刷新了,因为刷新消息在队列中,按钮按不下了,因为鼠标消
息也还在队列中,键盘也不动作了,再到任务管理器中一看!程序是死掉了(假死)
 
to dcsdcs:Delphi5中的控件仍然是线程不安全的。
to Silicon:死循环的话是程序设计的问题,应该不在讨论范围之内,至于你说的要处理三十
分钟的过程,如果放在线程中也很恐怖啊,这三十分钟里岂不是其它的线程都要停下来等待?
那线程就完全没有必要了。一般来说,要用Synchronize来调用的过程,基本上都是用来操作
一下VCL的,例如显示信息之类的,不可能会长时间占用VCL。
 
我也认为,PostMessage可能会在多个县城密集发送的时候导致消息的丢失。
同时,我觉得有一个问题,你用的PostMessage中参数是FStr的指针,问题
是,PostMessage是不等待消息处理结束的,那么,如果你的Thread的Execute
不是现在的循环执行的话,很可能,在等待处理消息的时候,你这个Thread
就结束了,FStr也已经被释放了,导致传递过去的指针失效。
如果用SendMessage的话呢,他又必须在消息处理完毕后才能返回,对于本
线程而言,其实也是中断了,跟Synthronize差不多。不过,我还是喜欢用
SendMessage+WM_COPYDATA,这样,可以传递复杂点的参数。
如果有可能的话,我倒是觉得,看看Synthronize到底用的是SendMessage
还是PostMessage应该会比较有趣。
一家之言,如果有什么地方说得不对,还望指正。
 
我对教父这种严谨的态度表示致敬!!!!!
TThread.Synchronize no longer uses a global window (ThreadWindow in D5's
classes.pas) to coordinate with the main VCL thread. Instead,
a TList, protected by a critical section, stores the events, and a new
procedure in classes.pas, called CheckSynchronize, must be called at
regular intervals to make Synchronize work. CheckSynchronize is
a function which returns TRUE if at least one TThread had an event in
the Synchronize queue when it was called.
使用synchronize是因为当你使用从VCL继承过来的对象时,他们的特性和方法并不能保证
是线程安全的,即,访问特性货方法也许会执行一些使用一些没有被其他线程保护的内存。
因此,为VCL对象的访问设了一个主VCL线程。这是处理应用程序中组件接收的所有的
Windows消息的线程。 如果所有对象是在同一个线程中访问特性和方法,就不必担心会发生
冲突。为了使用主VCL线程,产生一个分离的过程来执行需求的操作,在你的线程中使用
Synchronize方法来调用这个分离的过程。Synchronize等待主VCL线程进入消息循环,然后
执行相应的方法。

 
再致教父:
三十分只是一个例子,我就碰到这样的一个例子,说出来你说知道了。
我做的那个多媒体播放程序,一边在播放视频文件,一边在显示器上做特效显示,
播放部分的代码很短,执行的时间也很短,但是频率非常快,不到一秒要执行一次;而特效
代码执行时间片效多,如每两秒完成一个过程。好了,如果把这个部分做在一个程序中那完
蛋了,在做特效显示的时候,播放过程不能执行,只有等做完特效后再执行,这时候视频播
放就断断续续了!
这是事实,不信下次过来看看!没话说了吧!:)
 
to DreamTiger:SendMessage的问题我也考虑过了,确实它的效果应该是等同于Synchronize
的。你说的FStr被释放的问题确实存在,不知有没有好的方法解决?
有没有办法证明PostMessage会丢失消息?
 
to Silicon:
你那是两个线程,而且不用访问同一个VCL,所以根本不存在同步的问题。 :)
 
to Chenlili:非常感谢!!我一直都没有买D5的书 :(
 
三致教父:
不是两个线程,是你所说的两个消息处理过程,一个消息驱动播放,一个消息驱动特显示,
不管是每个消息做一个线程,还是不用线程也好,都不行。我最后的解决办法是将特显代
码放到一个线程的中用Synchronize()就搞定了,用消息驱就断断续续!
 
to Silicon:我还是觉得你说的例子跟我说的不是一回事。你的例子不存在两个以上的线程
同时访问一个VCL,你试试创建两个同样的线程实例来访问VCL看看。 :)
不过你的那个例子确实不能用消息来解决,只能用线程。
 
DreamTiger说的问题可以通过在线程中开内存,在消息处理函数中free的方法解决。
另外我觉得无论是Synchronize还是SendMessage/Postmessage,它们都有个共同的毛病,
它们对你所做的所有操作都是串行的,假设你的线程要操作两个Memo,线程A,B操作Memo1,
线程C,D操作Memo2,用Synchronize,SendMessage,Postmessage,这四个线程彼此都是
串行的,可实际上A/B与C/D之间是可以并行的,所以我认为最好的办法是用CreateEvent/
WaitForSingleObject的方法,为两个Memo各自定义一个Event,需要发送数据时就SetEvent,
这样A/B与C/D就可以并行工作起来了。(实际上Synchronize就用了Event,只不过它把
所有调用都看成是同一个Event).
 
《D5开发人员指南》P310:
用消息来同步
可以利用在线程之间使用消息同步以替代TThread.Synchronize()方法。
可以使用API函数SendMessage()或PostMessage()来发送消息。
PostMessage返回一个布尔值,表示消息是否已被放到消息队列中。
如前面几位所言,线程在消息尚未到达之间可能已经中止,则该消息将被抛弃,故PostMessage存在失丢失消息的可能。
 
唉!搞半日原来是说线程访问同一个VCL的问题,我以为是说是讨论把代码放在线程里还是
放在主程序中的问题!仔细看了一下你的程序,才搞明白是什么回事。
 
各位,实在是不好意思,上述代码实际上有问题,问题还是出去PostMessage上,
我开始写代码的时候是用SendMessage的,测试当然没有问题,后来突然想到SendMessage
还是要等待返回,所以改为PostMessage了,结果没有再测试了,而实际上用了PostMessage
后线程并不能得到同步,而且程序最终会崩溃,还不如不用Synchronize,真是奇怪。
既然只能用SendMessage,那么从效率上来说跟Synchronize应该没有太大的差别,我这个
问题也就没什么意义了,就此结束吧,谢谢各位的参与!
 
后退
顶部