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

    本人最近才迷恋上ROS(Robot Operating System),准确的说应该是6月中旬,具体的记不清了(可能是年纪大了,容易健忘).对于一个电子DIY的狂热爱好者来说,我在校的梦想就是做一个属 ...

  2. NOI2005 聪聪和可可

    Sol 记忆化搜索. \(f[u][v]\) 表示聪聪在 \(u\) ,可可在 \(v\) ,聪聪抓到可可的期望. 预处理出 \(u\) 到 \(v\) 最短路径编号最小的点,记为 \(g[u][v] ...

  3. cacti错误

    cacti 错误:CMDPHP: Poller[0] ERROR 解决方案: 找到错误表 desc 表名: 修复此表 mysqlcheck -A -o -r -p -u用户名

  4. visual studio 2012 插件

    下面来分享几个好用的插件:直接在Tools-Extensions and Updates-Online中搜索就可以安装了 (中文版位于:菜单-工具-扩展和更新-联机-Visual Studio库) 1 ...

  5. sizeof和strlen的区别

    一.sizeof    sizeof(...)是运算符,而不是一个函数.    sizeof操作符的结果类型是size_t,在头文件中typedef为unsigned int,其值在编译时即计算好了, ...

  6. ACM/ICPC 之 昂贵的聘礼-最短路解法(POJ1062)

    //转移为最短路问题,枚举必经每一个不小于酋长等级的人的最短路 //Time:16Ms Memory:208K #include<iostream> #include<cstring ...

  7. MyISAM 调度(优先级)的一些优化【转】

    MySQL的MyISAM引擎现在越来越被淡化了,但是还是有必要再温习总结一下的. 允许你改变语句调度的优先级,它可以使来自多个客户端的查询更好地协作,这样单个客户端就不会由于锁定而等待很长时间.改变优 ...

  8. codeforces 505A. Mr. Kitayuta's Gift 解题报告

    题目链接:http://codeforces.com/problemset/problem/505/A 题目意思:给出一个长度不大于10的小写英文字符串 s,问是否能通过在字符串的某个位置插入一个字母 ...

  9. 【leetcode】 Interleaving String (hard)

    Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2. For example,Given:s1 = ...

  10. IOS - 响应者链条

    简单来说就是:一级一级的找到响应的视图,如果没有就传给UIWindow实例和UIApplication实例,要是他们也处理不了,就丢弃这次事件... 对于IOS设备用户来说,他们操作设备的方式主要有三 ...