尽管多线程能够解决许多问题,但是同时它又给我们带来了很多的问题。其中主要的问题就是:对全局变量或句柄这样的全局资源如何访问?另外,当必须确保一个线程中的某些事件要在另一个线程中的其他时间之前(或之后)发生时,该怎么办?这里将讲解通过使用由 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的更多相关文章

  1. Java多线程系列——线程池原理之 ThreadPoolExecutor

    ThreadPoolExecutor 简介 ThreadPoolExecutor 是线程池类. 通俗的讲,它是一个存放一定数量线程的线程集合.线程池允许多个线程同时运行,同时运行的线程数量就是这个线程 ...

  2. OS之进程管理---多线程模型和线程库(POSIX PTread)

    多线程简介 线程是CPU使用的基本单元,包括线程ID,程序计数器.寄存器组.各自的堆栈等,在相同线程组中,所有线程共享进程代码段,数据段和其他系统资源. 传统的的单线程模式是每一个进程只能单个控制线程 ...

  3. 线程局部存储(TLS)

    线程局部存储(TLS) 2011-10-11 09:59:28|  分类: Win32---API |  标签:tls   |举报 |字号 订阅   什么是线程局部存储 众所周知,线程是执行的单元,同 ...

  4. 线程局部存储tls的使用

    线程局部存储(Thread Local Storage,TLS)主要用于在多线程中,存储和维护一些线程相关的数据,存储的数据会被关联到当前线程中去,并不需要锁来维护.. 因此也没有多线程间资源竞争问题 ...

  5. Delphi 中多线程同步的一些处理方法

    Delphi 中多线程同步的一些处理方法   当创建了多个线程,并且多个线程都要访问同一资源,,就有可能出现混乱,于是用Synchronize来控制,使同一时间只有一个线程使用那部分资源,Synchr ...

  6. C#多线程之线程池篇1

    在C#多线程之线程池篇中,我们将学习多线程访问共享资源的一些通用的技术,我们将学习到以下知识点: 在线程池中调用委托 在线程池中执行异步操作 线程池和并行度 实现取消选项 使用等待句柄和超时 使用计时 ...

  7. C#多线程之线程同步篇3

    在上一篇C#多线程之线程同步篇2中,我们主要学习了AutoResetEvent构造.ManualResetEventSlim构造和CountdownEvent构造,在这一篇中,我们将学习Barrier ...

  8. Java多线程开发系列之四:玩转多线程(线程的控制1)

    在前文中我们已经学习了:线程的基本情况.如何创建多线程.线程的生命周期.利用已有知识我们已经可以写出如何利用多线程处理大量任务这样简单的程序.但是当应用场景复杂时,我们还需要从管理控制入手,更好的操纵 ...

  9. 多线程系列 线程池ThreadPool

    上一篇文章我们总结了多线程最基础的知识点Thread,我们知道了如何开启一个新的异步线程去做一些事情.可是当我们要开启很多线程的时候,如果仍然使用Thread我们需要去管理每一个线程的启动,挂起和终止 ...

随机推荐

  1. OpenCv皮肤检测-HSV分离

    HSV皮肤检测 // 进行肤色检测 void SkinDetect(IplImage* src, IplImage* dst) { // 创建图像头 IplImage* hsv = cvCreateI ...

  2. OpenCV加载图像并显示

    从文件中读取一直一张图片,并加载出来 代码: #include "stdafx.h" #include "iostream" using namespace s ...

  3. (备忘)android清单文件中<meta-data>标签,以及<intent-filter>下的<data>标签及其他标签

    1.metadata可以写在application下也可以写在activity下,作为全局或activity内共享的数据 以键值对形式保存 <meta-data android:name=&qu ...

  4. Delphi 中的 procedure of object

    转载:http://www.cnblogs.com/ywangzi/archive/2012/08/28/2659811.html 总结:TMyEvent = procedure of object; ...

  5. BZOJ 1485: [HNOI2009]有趣的数列

    Description 求长度为 \(2n\) 的序列.要求 1. \(a_1<a_3<a_5<...<a_{2n-1}\) . 2. \(a_2<a_4<a_6& ...

  6. hdu 5442 (ACM-ICPC2015长春网络赛F题)

    题意:给出一个字符串,长度是2*10^4.将它首尾相接形成环,并在环上找一个起始点顺时针或逆时针走一圈,求字典序最大的走法,如果有多个答案则找起始点最小的,若起始点也相同则选择顺时针. 分析:后缀数组 ...

  7. nginx服务器设置url的优雅链接

    对于LNMP这样架构的网站来说,一般都是基于php框架开发,php框架一般都会讲究优雅链接,比如Laravel,CodeIgniter,ThinkPHP等都是支持这种链接模式的,在服务器配置上也叫作u ...

  8. percona-toolkit 之 【pt-summary】、【pt-mysql-summary】、【pt-config-diff】、【pt-variable-advisor】说明

    摘要: 通过下面的这些命令在接触到新的数据库服务器的时候能更好更快的了解服务器和数据库的状况. 1:pt-summary:查看系统摘要报告 执行: pt-summary 打印出来的信息包括:CPU.内 ...

  9. MySQL表字段长度的限制

    在MySQL建表时,遇到一个奇怪的现象: root::>CREATE TABLE tb_test ( ) NOT NULL, ) DEFAULT NULL, ) DEFAULT NULL, ) ...

  10. 配置IIS,Apache,PHP过程中遇到的一些问题

    下载了eclipse的最新版本,并且添加了PHP插件.为了支持多语言,决定采用UTF-8编码.但是在开发的过程中,发现代码的自动提示帮助信息显示的是乱码,PHP源文件及注释,均正常.在网上查了很多资料 ...