http://embarcadero.newsgroups.archived.at/public.delphi.rtl/201112/1112035763.html

> Hi,
>
> What is the difference between these two definitions:
>
>  TThreadMethod = procedure of object;
>  TThreadProcedure = reference to procedure;
>
> Please can you try to explain simply :)

Well, it is not exactly a simple subject ;-)

TThreadMethod declares a method pointer type.

Internally, a method pointer is represented as a record of two pointers (there is a type TMethod for that).

TMethod = record
    code: pointer;
    data: pointer
  end;

If you declare a variable of a method pointer type you can only assign
it a method of an object. Wayne's example is wrong here since it uses
the @ operator. You do not use that for assigning a method to a method
pointer variable.

TMyClass = class
    private
        MyMethod: ThreadMethod;
        procedure Foo;

MyMethod := Foo;

On this assignment the compiler internally executes

TMethod(MyMethod).Code := @TMyClass.Foo;
  TMethod(MyMethod).Data := self;  // actually the address of the object Foo belongs to

Calling the method through the method pointer will pass the Data value
as the hidden Self parameter to Foo. So you can only use a method
pointer safely as long as the object the pointer refers to still exists.

TThreadProcedure = reference to procedure;

This declares a new-fangled thing called an anonymous method, but it is
in fact more generally useful than the use  Wayne showed. To a variable
of type TThreadProcedure you could assign not only a nameless procedure
constructed on the fly, but also an existing method of some object, or
even a standalone procedure. The compiler does a lot more work behind
the scene for these types. It creates a whole class with a matching
interface type, creates an instance, stores any local variables the
anonymous method refers to into this instance, implements the anonymous
method as a method of this class, calls it, cleans up afterwards.
"reference to x" types are not simple types, unlike method or function
pointer types. They have considerable overhead in terms of code size
and execution, but are a lot more flexible in turn.

Anonymous methods are Delphi's implementation of a computer science
concept called a closure (see
http://en.wikipedia.org/wiki/Closure_%28computer_science%29 )

--

http://www.cnblogs.com/del/archive/2011/05/18/2049913.html

先看一个非多线程的例子, 代码执行时不能进行其它操作(譬如拖动窗体):

{自定义方法: 在窗体上绘制...}
procedure MyMethod;
var
i: Integer;
begin
for i := to do
begin
Form1.Canvas.Lock;
Form1.Canvas.TextOut(, , IntToStr(i));
Form1.Canvas.Unlock;
end;
end; {调用上面的自定义方法}
procedure TForm1.Button1Click(Sender: TObject);
begin
MyMethod;
end;

修改为多线程(只修改一行代码):

procedure MyMethod;
var
i: Integer;
begin
for i := to do
begin
Form1.Canvas.Lock;
Form1.Canvas.TextOut(, , IntToStr(i));
Form1.Canvas.Unlock;
end;
end; procedure TForm1.Button1Click(Sender: TObject);
begin
TThread.CreateAnonymousThread(MyMethod).Start; //!!!
end;

代码分析:

1、TThread 现在增加了许多 class 方法(直接通过类名调用的方法), TThread.CreateAnonymousThread() 就是比较有用的一个.

2、顾名思义, CreateAnonymousThread 是建立匿名线程对象, 它的参数是我们需要在线程中执行的方法.

3、但 CreateAnonymousThread 建立线程后是挂起的, 需要手动运行它; 后面的 Start 方法就是用来唤醒线程的.

4、(以前)唤醒线程还可以使用 Resume 方法或 Suspended 属性(Suspended := False;); 但它们即将被废弃了, 现在应使用 Start 来启动线程.

CreateAnonymousThread 的参数类型 TProc 是匿名方法(reference), 所以代码可以简写为:

procedure TForm1.Button1Click(Sender: TObject);
begin
TThread.CreateAnonymousThread( //直接写入方法体
procedure
var
i: Integer;
begin
for i := to do
begin
Canvas.Lock;
Canvas.TextOut(, , IntToStr(i));
Canvas.Unlock;
end;
end //此处无分号
).Start;
end;

延时执行:

var
myThread: TThread; procedure TForm1.FormCreate(Sender: TObject);
begin
myThread := TThread.CreateAnonymousThread(
procedure
var
i: Integer;
begin
for i := to do
begin
Canvas.Lock;
Canvas.TextOut(, , IntToStr(i));
Canvas.Unlock;
end;
end
);
end; procedure TForm1.Button1Click(Sender: TObject);
begin
myThread.Start;
end;

http://www.uweraabe.de/Blog/2011/01/30/synchronize-and-queue-with-parameters/

Synchronize and Queue with Parameters

Posted on January 30, 2011 by Uwe Raabe

In Delphi’s TThread class there is Synchronize to call a method in the context of the GUI thread.

This is necessary in case, for example, you want to update a progressbar or a status label in a form, because the VCL is not thread safe.

While Synchronize is a blocking method (i.e. the thread code continues when the GUI thread has finished the method call),

recent versions of Delphi introduced a non-blocking Queue method.

The drawback of using Synchronize is that it is very cumbersome to handle parameters during such a call.

The standard solution is to use fields in the TThread descendant holding the parameters during Synchronize,

but this won’t work properly with Queue.

As Queue is non-blocking there might be multiple calls to Queue waiting to be processed,

which often cannot share the same field.

Worse, using Queue access to these fields have to be thread safe, too.

The queuing thread may just set this field for the next call to Queuewhile a previous Queue call is executed

in the main thread just using this field.

As this problem doesn’t exist with Synchronize, switching

from Synchronize to Queue just has to be done more careful.

One way to deal with parameters for Synchronize is the use of Anonymous Methods.

There is an overloaded version of Synchronize and Queue taking a TThreadProcedure as parameter:

TThreadMethod = procedure of object;
TThreadProcedure = reference to procedure; procedure Queue(AMethod: TThreadMethod); overload;
procedure Synchronize(AMethod: TThreadMethod); overload;
procedure Queue(AThreadProc: TThreadProcedure); overload;
procedure Synchronize(AThreadProc: TThreadProcedure); overload;

A simple example for a parametrized call to Synchronize could be like this:

type
TMyProgressEvent = procedure(PercentComplete: Integer) of object; TMyThread = class(TThread)
private
FOnMyProgress: TMyProgressEvent;
protected
procedure CallMyProgress(PercentComplete: Integer);
procedure Execute; override;
public
property OnMyProgress: TMyProgressEvent
read FOnMyProgress
write FOnMyProgress;
end; procedure TMyThread.CallMyProgress(PercentComplete: Integer);
begin
if GetCurrentThreadId = MainThreadID then begin
if Assigned(FOnMyProgress) then
FOnMyProgress(PercentComplete);
end
else begin
Synchronize(
procedure
begin
CallMyProgress(PercentComplete);
end);
end;
end; procedure TMyThread.Execute;
var
I: Integer;
begin
for I := to do begin
CallMyProgress(I);
Sleep();
end;
end;

The magic is that the Anonymous Method captures the variable PercentComplete.

Thus it will be totally safe to use Queue instead of Synchronize here.

I’m using a little trick here to ensure that the critical code is called inside the GUI thread without having a separate method.

The ID of the GUI thread is stored in MainThreadID, so I just check if the result of the function GetCurrentThreadID matches

this value and call Synchronize otherwise.

One note using Queue:

when the thread is freed, all pending Queue calls are removed.

So if your code depends on all these calls being handled in the GUI thread

you have to make sure that the thread instance lives at least that long.

http://sergworks.wordpress.com/2010/09/14/synchronization-in-delphi-tthread-class/

Synchronization in Delphi TThread class

VCL sources is a very interesting reading, if you have a time for it. Many Delphi programmers (like me) use VCL TThread class as a simple wrapper for WinAPI kernel thread object, avoiding to use Synchronize and Queue methods by using a custom message processing instead. Still it is worthwhile to understand the inner workings of VCL thread synchronization.

Disclaimer: I am currently using Delphi 2009, so some details of the next study may be different for the other Delphi (VCL) versions.

TThread class affords two ways for a background (“worker”) thread to execute a code in context of the main (GUI) thread. You can use either one of the Synchronize method overloads which force the worker thread into a wait state until the code is executed, or else you can use one of the Queue method overloads that just put the code into the main thread’s synchronization queue and allows a worker thread to continue. The definitions of the Synchronize and Queue methods in TThread class are:

type
TThreadMethod = procedure of object;
TThreadProcedure = reference to procedure; procedure Synchronize(AMethod: TThreadMethod); overload;
procedure Synchronize(AThreadProc: TThreadProcedure); overload;
class procedure Synchronize(AThread: TThread; AMethod: TThreadMethod); overload;
class procedure Synchronize(AThread: TThread; AThreadProc: TThreadProcedure); overload; procedure Queue(AMethod: TThreadMethod); overload;
procedure Queue(AThreadProc: TThreadProcedure); overload;
class procedure Queue(AThread: TThread; AMethod: TThreadMethod); overload;
class procedure Queue(AThread: TThread; AThreadProc: TThreadProcedure); overload;

We see that Synchronize and Queue methods can be used either with ordinary methods (TThreadMethod) or anonymous methods (TThreadProcedure).

Both Synchronize and Queue methods internally call a private Synchronize method overload, which is examined in detail next.

class procedure TThread.Synchronize(ASyncRec: PSynchronizeRecord; QueueEvent: Boolean = False); overload;

The first argument is a pointer to TSynchronizeRecord structure (see definition below) which in turn contains a pointer to a code to be executed;

a code pointer can be either ordinary method pointer (TThreadMethod) or anonymous method’s reference (TThreadProcedure)

type
PSynchronizeRecord = ^TSynchronizeRecord;
TSynchronizeRecord = record
FThread: TObject;
FMethod: TThreadMethod;
FProcedure: TThreadProcedure;
FSynchronizeException: TObject;
end; TSyncProc = record
SyncRec: PSynchronizeRecord;
Queued: Boolean;
Signal: THandle;
end;

The second argument (QueueEvent) is False for Synchronize overloads and True for Queue overloads.

Now back to the TThread.Synchronize code:

var
SyncProc: TSyncProc;
SyncProcPtr: PSyncProc;
begin
if GetCurrentThreadID = MainThreadID then begin
if Assigned(ASyncRec.FMethod) then
ASyncRec.FMethod()
else if Assigned(ASyncRec.FProcedure) then
ASyncRec.FProcedure();
end

The above is a kind of “fool protection”.

If Synchronize (or Queue) is called from the main thread, the correspondent code is just executed.

The rest of the method’s code is dealing with the worker’s thread calls:

else begin
if QueueEvent then
New(SyncProcPtr)
else
SyncProcPtr := @SyncProc;

For Synchronize calls we use stack variable to store TSyncProc structure;

we can’t use stack for asynchronous Queue calls, so we allocate a new TSyncProc variable on the heap.

if not QueueEvent then
SyncProcPtr.Signal := CreateEvent(nil, True, False, nil)
else
SyncProcPtr.Signal := ;

For Synchronize calls we need a signaling event to wake up the worker thread after the main thread have finished executing the synchronized code;

for the Queue calls we does not interfere into thread scheduling, so we need not a signaling event.

try
EnterCriticalSection(ThreadLock);
try
SyncProcPtr.Queued := QueueEvent;
if SyncList = nil then
SyncList := TList.Create;
SyncProcPtr.SyncRec := ASyncRec;
SyncList.Add(SyncProcPtr);
SetEvent(SyncEvent);
if Assigned(WakeMainThread) then
WakeMainThread(SyncProcPtr.SyncRec.FThread);

We are preparing an information for the main thread about the code to execute.

I will discuss how (and where) the main thread finds out that it has some worker code to execute a little later.

if not QueueEvent then begin
LeaveCriticalSection(ThreadLock);
try
WaitForSingleObject(SyncProcPtr.Signal, INFINITE);
finally
EnterCriticalSection(ThreadLock);
end;
end;

A very interesting code fragment (executed only for Synchronize calls).

We release the ThreadLock critical section (to enable other threads to execute Synchronize or Queue calls),

wait the main thread to execute the code and enter the ThreadLock again.

The above code fragment is exactly the job for a condition variable.

Condition variable is a synchronization primitive first introduced in Windows Vista (in Windows part of the world;

it probably always existed in Unix/Linux).

Have VCL supported only Vista and above Windows versions, the above code fragment could be replaced like this:

if not QueueEvent then begin
SleepConditionVariableCS(CondVar, ThreadLock, INFINITE);
end;

(well, not so simple; we should initialize the CondVar condition variable before we can use it. See msdn for details).

The use of the conditional variables makes the code more effective

(thread enters a wait state and releases the specified critical section as an atomic operation) and more readable.

      finally
LeaveCriticalSection(ThreadLock);
end;
finally
if not QueueEvent then
CloseHandle(SyncProcPtr.Signal);
end;
if not QueueEvent and Assigned(ASyncRec.FSynchronizeException) then
raise ASyncRec.FSynchronizeException;
end;
end;

That is all.

Both Synchronize and Queue calls prepare a TSyncProc structure containing a pointer to the code to be executed by the main thread,

insert the structure into the global SyncList list, and with Synchronize call a worker thread also enters a wait state

until the main thread sets a signaling event in the TSyncProc structure.

Now about how the main thread finds out that the worker threads have prepared TSyncProc structures for him.

The answer is WakeMainThread global variable defined in Classes.pas:

var
WakeMainThread: TNotifyEvent = nil;

in the above Synchronize procedure we have

if Assigned(WakeMainThread) then
WakeMainThread(SyncProcPtr.SyncRec.FThread);

The WakeMainThread is assigned during the application initialization by the following code:

procedure TApplication.WakeMainThread(Sender: TObject);
begin
PostMessage(Handle, WM_NULL, , );
end;

and WM_NULL message is processed in application window procedure as follows:

procedure TApplication.WndProc(var Message: TMessage);
[..]
with Message do
case Msg of
[..] WM_NULL:
CheckSynchronize;
[..]

We see that every Synchronize or Queue method call posts WM_NULL message to the hidden application window;

the WM_NULL message is processed by calling CheckSynchronize procedure.

CheckSynchronize procedure scans the global SyncList list of the TSyncProc structures, executes the code pointed by them and,

for Synchronize calls only, sets a signaling event in the TSyncProc structures after executing the code to wake up a worker thread.

What is the resume of the above analysis?

Both Synchronize and Queue methods internally use window message processing (in the hidden application window),

and in fact there is a little difference with custom message processing here.

Due to design reasons a custom message processing is preferable when designing multithreaded components

(with their own window procedures, created for example by Classes.AllocateHWnd function).

Usually there is no practical reason to use custom message processing when designing multithreaded applications –

Synchronize and Queue methods of the TThread class are just as good.

Custom Messages had been useful until the introduction of TThreadProcedure.

Before its introduction if you want to pass additional parameters to the Syncronized Method

you have to introduce additional field/properties in your TThread descendant classes or to use a custom message in order to use WPARAM and LPARAM.

The use of TThreadProcedure allows the use anonymous method that can capture parameters as anonymous method’s local variable.

The handling of the main thead’ waking up the has changed since delphi 7.

In delphi 7, and maybe in other version, the CheckSynchronize is called in the Application.

ProcesseMessages function.

It means that it is called only if your thread is hosted in an applicaton that has a message pump implemented by a delphi’s TApplication.

This does not work if you make an ActiveX that has secondary threads implemented using TThread that is hosted in a “foreign ” host (Es VB app).

The new implementation does not suffer of this problem.

Regards,
Stefano Moratto

There is no need to write a custom messaging solution from scratch,

you can build one using the TThread.Queue method.

I have done this myself such that I can “post” any data structures

(simple types, compount types, objects, etc) from background threads to the main thread.

TThread.Queue is one of those undocumented hidden gems that was introduced in Delphi 2005 that I cannot do without.

Synchronization in Delphi TThread class : Synchronize, Queue的更多相关文章

  1. 使用delphi TThread类创建线程备忘录

    备忘,不常用经常忘了细节 TMyThread = class(TThread) private { Private declarations } protected procedure Execute ...

  2. TExternalThread TThread -- Delphi -- Cannot terminate an externally created thread ?

    Cannot terminate an externally created thread ? The VCL has a new TExternalThread class which derive ...

  3. Synchronize执行过程

    Synchronize执行过程及原理 在windows原生应用程序开发中,经常伴随多线程的使用,多线程开发很简单,难点就是在于线程的同步,在Delphi中提供了VC中不具备的一个过程Synchroni ...

  4. Synchronizing Threads and GUI in Delphi application

    Synchronizing Threads and GUI   See More About delphi multithreading tthread class user interface de ...

  5. Delphi多线程详解

    (整理自网络) Delphi多线程处理 1-1多线程的基本概念 WIN 98/NT/2000/XP 是个多任务操作系统,也就是:一个进程可以划分为多个线程,每个线程轮流占用CPU 运行时间和资源,或者 ...

  6. delphi 面向对象实用技能教学二(封装)

    面向对象编程手法,是一项综合技能,单独把谁拿出来说都不合适.本次重写 TSimpleThread ,使其能在 D7 下运行. 基于 TSimpleThread ,重磅推出 TSimpleUI.ExeP ...

  7. c++builder XE8 线程 Thread

    thread Thread  c++builder XE8 / RAD 10 Settle delphi TThread.CreateAnonymousThread(MyMethod).Start; ...

  8. Introduction to Parallel Computing

    Copied From:https://computing.llnl.gov/tutorials/parallel_comp/ Author: Blaise Barney, Lawrence Live ...

  9. Windows消息、绘图与多线程

    有一个项目,一旦点下按钮后,用死循环不停的读数据,读出后立刻用可视化的方法显示.如果不采用多线程的方法,程序运行都正确,但无法关闭窗口,不清楚是窗口无法通过关闭按钮来接受Windows消息,还是接受了 ...

随机推荐

  1. myeclipse安装

    http://www.cnol.cc/2013/1427.html(我用这个) http://www.myeclipseide.cn/windows.html http://download.csdn ...

  2. UIScrollView 不能滚动的问题

    uiscrollview是开发sdk自带的控件, 在使用的时候,发现滚动不了, 最常山见的原因是 contentSize 这个属性,比uiscrollview的frame要小...所以无需滚动,自然就 ...

  3. The Network Adapter could not establish the connection解决办法

    用 oracle net manager 将监听改为IP地址,将服务命名也改为IP地址,然后数据库连接改为IP地址方式不要用localhost

  4. Android SDK更新 Connection to http://dl-ssl.google.com refused 解决方法

    问题描述 使用SDK Manager更新时出现问题Failed to fetch URL https://dl-ssl.google.com/android/repository/repository ...

  5. 【LeetCode 234】Palindrome Linked List

    Given a singly linked list, determine if it is a palindrome. 思路: 用快慢指针找到链表中点,反转后半部分链表,然后与前半部分进行匹配,随后 ...

  6. selenium python (七)层级定位(二次定位)

    #!/usr/bin/python# -*- coding: utf-8 -*-__author__ = 'zuoanvip' #在实际测试过程中,一个页面可能有多个属性基本相同的元素,如果要定位到其 ...

  7. Windows字符集的统一与转换

    以前也零零散散看过一些字符编码的问题,今天看来这边博客,感觉很多东西都总结在里面,非常值得学习! 一.字符集的历史渊源 在Windows编程时经常会遇到编码转换的问题,一直以来让刚接触的人摸不着头脑. ...

  8. Redhat=》中文

    我的redhat安装时没有提示语言选项,由于工程需要,支持汉字是不可避免的,因此就必须安装中文输入法. 安装中文包 将系统光盘镜像文件连接至计算机,我的镜像是RHEL5.1的,先将光盘挂载至/mnt目 ...

  9. Hadoop概念学习系列之hadoop生态系统闲谈(二十五)

    分层次讲解 最底层平台 ------->hdfs  yarn  mapreduce spark 应用层-------->hbase  hive   pig   sparkSQL    nu ...

  10. MySQL [Warning] Can’t create test file xxx lower-test(转)

    add by zhj:修改的数据库的datadir,然后数据库就无法启动了,错误如下 2014-12-11 16:22:57 26309 [Warning] Can't create test fil ...