Delphi管理多线程之线程局部存储:threadvar

摘要:
2)第二种方式就是把有关信息保存在各自的线程对象中3)第三种方式就是用Delphi的关键字threadvar来声明变量,以利用操作系统级的线程局部存储1.把信息保存在TThread派生对象中将相关信息保存在TThread派生对象中,这是一种线程局部存储可选的技术。可问题是,有可能有两个或更多的线程存在。Delphi利用关键字threadvar封装此功能。下面是局部变量GlobalStr的声明:threadvarGlobalStr:String;下面演示了线程局部存储的用法。

尽管多线程能够解决许多问题,但是同时它又给我们带来了很多的问题。其中主要的问题就是:对全局变量或句柄这样的全局资源如何访问?另外,当必须确保一个线程中的某些事件要在另一个线程中的其他时间之前(或之后)发生时,该怎么办?这里将讲解通过使用由 Delphi提供的线程局部存储和 A P I为线程提供同步的方法。

这里先讲线程局部存储,下一篇再讲线程同步

线程局部存储

由于每个线程都代表一个不同的执行路径,因此,最好有一种只限于一个线程内部使用的数据存储方式。要实现上述目的有三种方式:

1)第一种也是最简单的一种方式是局部变量((基于栈)。

由于每个线程都在各自的栈中,各个线程都将有一套局部变量的副本,这样,就不会互相影响。

2)第二种方式就是把有关信息保存在各自的线程对象中

3)第三种方式就是用Delphi的关键字 threadvar 来声明变量,以利用操作系统级的线程局部存储

1.把信息保存在TThread派生对象中

将相关信息保存在TThread派生对象中,这是一种线程局部存储可选的技术。相对于使用关键字threadvar的技术,这种方式更加简单、更加有效。例如,你可以在一个线程对象中加入下列信息

type
    TMyThread = class(TThread)
    private
        FLocalInt: Integer;
        FLocalStr: String;
        ..
        ..
    end;

提示:由于访问线程对象中的数据比访问线程局部变量快10倍,因此,你应当尽可能地把线程专用的信息保存在线程对象中。对于那些只在过程或函数的生存期有意义的变量,应当把它们声明为局部变量

2.threadvar:API线程局部存储

在前面讲到:虽然对于局部变量,在每个线程中都有一个副本,然而应用程序的全局变量是被所有线程所共享的。例如,假设现在还有一个用于设置和显示一个全局变量的值的过程。如果传递一个字符串给它,就设置这个全局变量;如果传递一个空字符串给它,就显示这个全局变量的值。这个过程定义如下:

var
    GolbalStr: String;
procedure SetShowStr(const S: String);
begin
    if S = '' then
        MessageBox(0, PChar(GlobalStr), 'The string is ...', MB_OK)
    else
        GlobalStr:= S;
end;

如果在某段代码中调用这个过程,则可能出现一些问题。因为在调用它时,可以先设置而后显示。可问题是,有可能有两个或更多的线程存在。当一个线程调用此过程来设置字符串时,而另一个线程也可能调用此过程来设置GlobalStr 变量的值。当操作系统把CPU时间又分配给前一个线程时,该线程所设置的值有可能已经令人失望的丢失了

为了解决这个问题,Win32 提供了一种称为线程局部存储的方式,它能使你在第一个运行的线程中创建一个全局变量的拷贝。Delphi利用关键字 threadvar封装此功能。

在threadvar关键字下你可以声明任何局部存储的变量。下面是局部变量GlobalStr的声明:

threadvar
    GlobalStr: String;

下面演示了线程局部存储的用法。在程序的主窗体上有一个按钮,单击此按钮,就设置并显示 GlobalStr变量的值。然后再创建一个线程,GlobalStr变量的值有被设置并显示。在创建了一个线程之后,从主线程再次设置并显示GlobalStr变量的值

先后用var 和 threadvar来声明 GlobalStr变量并运行此程序,你会在输出中看到不同

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

type
    TMainForm = class(TForm)
        Button1:TButton;
        procedure Button1Click(Sender: TObject);
    private
        {...}
    public
        {...}
    end;

var
    MainForm: TMainForm;

implementation
{$R *.DFM}

var
//threadvar
    GlobalStr: String;

type
    TTLSThread = class(TThread)
    private
        FNewStr: String;
    protected
        Procedure Execute; override;
    public
        cpnstructor Create(Const ANewStr: String);
    end;

procedure SetShowStr(const S: String);
begin
    if S = '' then
        MessageBox(0, PChar(GlobalStr), 'The string is ...', MB_OK)
    else
        GlobalStr:= S;
end;

constructor TTLSThread.Create(const ANewStr: String);
begin
    FNewStr:= ANewStr;
    inherited Create(False);
end;

procedure TTSThread.Execute;
begin
    FreeOnTerminate:= True;
    SetShowStr(FNewStr);
    SetShowStr('');
end;

procedure TMianForm.Button1Click(Sender: TObject);
begin
    SetShowStr('Hello World');
    SetShowStr('');
    TTLSThread.Create('Dilbert');
    Sleep(100);
    SetShowStr('');
end;

end.

注意:演示程序中,在创建了线程之后,调用了一个Win32 API过程 Sleep()。此过程的声明如下

procedure Sleep(dwMillseconds: DWORD); stdcall;

Sleep()过程用来告诉操作系统,当前的线程在参数 dwMillseconds指定的时间内不需要分配任何CPU时间。插入这个调用是使很多的任务在发生时,使执行哪个线程有一些随机性

通常,可以把参数dwMillseconds设置为0,尽管,这并没有使当前的线程真的“睡眠”,但是它使操作系统把CPU时间分给其他优先级相等或者更高的线程

要小心Sleep()神秘的时间调整问题。Sleep()可能会是你的机器出现特别的问题。这种问题在另一台机器上可能无法再现

免责声明:文章转载自《Delphi管理多线程之线程局部存储:threadvar》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇cocos2d环境搭建iptables raw表下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

友盟官方文档

#  产品概述 关于分享和授权的sdk接口,我们在v4.4.0做出了巨大的改变,精简了接口调用的代码。并将分享授权,与评论等功能做出了隔离,使结构更加清晰。所以本版本的功能也只有分享与授权并无其他功能,请开发者注意。 注意:本文示例代码只有分享与授权功能,并对接口进行了调整,如需要原功能的开发者,请继续使用v4.3.0版本。 # 获取友盟Appkey 如果你...

Java高级开发_性能优化的细节

一、核心部分总结: 尽量在合适的场合使用单例【减负提高效率】 尽量避免随意使用静态变量【GC】 尽量重用对象,避免过多过常地创建Java对象【最大限度地重用对象】 尽量使用final修饰符【内联(inline)】 尽量使用局部变量【栈快】 尽量处理好包装类型和基本类型两者的使用场所【堆栈】 慎用synchronized,尽量减小synchronize的方...

Makefile写法入门心得

Makefile的写法入门心得   Makefile的出现使得编译过程极大地自动化了,省去了很多人工工作,非常方便。这里,小结一下写Makefile的一点小心得。嗯,对了,差点忘了一件事,同一个Makefile在不同的内核版本和Linux发行版本中不一定能发挥一样的作用,别人的Makefile可不见得就适用于自己的机器。 我先简单说一下我自己的机器: Ub...

【cocos2d-js官方文档】十八、Cocos2d-JS v3.0中的属性风格API

1. 新的API风格 我们直接来看看你可以如何使用Cocos2d-JS v3.0: 以前的API 新的API node.setPosition(x, y); node.x = x;node.y = y; node.setRotation(r); node.rotation = r; 如表格中可以看到的,设置position属性的函数调用...

CSS变量variable

前面的话 一直以来,CSS中是没有变量而言的,要使用 CSS 变量,只能借助 SASS 或者 LESS 这类预编译器。新的草案发布之后,直接在 CSS 中定义和使用变量不再是幻想了。本文将详细介绍CSS变量variable 基本用法 CSS 变量是由CSS作者定义的实体,其中包含要在整个文档中重复使用的特定值。使用自定义属性来设置变量名,并使用特定的var...

利用C#线程窗口调试多线程程序

       从网上的资料判断,调试多线程程序似乎就一下3种方法。 1、在日志的某个地方写日志文件。 优点:不会干扰程序的执行,特别是对网络的多线程通信。 缺点:每次都需要打开日志文件以查看进程运行的信息。 2、利用断点进行调试。 优点:直观,可以直接看到运行过程的值 缺点:在多个线程设置断点,可能让程序跳来跳去,还需要额外地分出一部分精力用来理清程序...