第七节:在多个线程时空中,把各自的代码塞到一个指定的线程时空运行
   
以 Ado 为例,常见的方法是拖一个 AdoConnection 在窗口上(或 DataModule 中),
再配合 AdoQuery ,DataSoure, DbGrid 等,就可以实现数据库的访问操作。
这种方式,可以理解为在主线程时空中访问数据库。
如果某一个查询或更新操作耗时较长,那么,界面将会假死。
 
1.在线程时空中访问数据库
 
其实,上文中提到的这个 AdoConnection 不单只能在主线程时空中访问,
亦可以在另一个多线程时空里访问。但有一个重要的前提:
即:任意时刻,只能是一个线程访问 AdoConnection !
可以是多线程时空访问,也可以是主线程时空访问,它们可以交替访问,但不能同时访问。
 
故此,我们可以定义一个 DataThread 线程时空,把其它线程时空中需要访问数据库的操作,
全部塞入到 DataThread 时空中。具体做法是在 DataThread 中设计一个接口,如下:
 
unit uJooThread;
interface
uses
  Classes, uFooThread;
type
 
  TJooThread = class(TFooThread)
  public
    procedure Synchronize(AProc: TThreadMethod);
  end;
 
implementation
 
{ TJooThread }
 
procedure TJooThread.Synchronize(AProc: TThreadMethod);
begin
  ExecProcInThread(AProc);
  // 再设计一个等待 AProc 执行结果功能。
end;
 
end.
 
这样既简化了其它线程访问数据库的操作,也避免了因为数据库操作耗时引起界面假死。
另外,如果访问数据库发生了异常,重新 Create AdoConnection 然后再连接的操作也在这一个线程中进行,
就不必考虑数据库操作的先后问题。如:不会出现需要访问数据库时, AoConnection 是损坏状态的情况。
也就是说,用这个线程,把访问数据库的操作进行了排队。
定义一个工作线程A, 先从数据库中取参数,再进行计算,然后把结果再更新到数据库。
如果有10个线程A实例,那么线程A访问数据库的时候,只需要把访问操作塞进 DataThread 中即可。
 
有朋友肯定会有疑问,为何不为每一个线程指定一个 AdoConnection 。这样编程将会变得简单。
一、数据库的连接是会消耗资源的,连接数是有上限的。
二、如果每个线程的 AdoConnection 产生的操作都是访问同一个表。
那么数据库自身会上锁,将它们变成串行执行,并不能提高效率。
 
2.多个客户端,同时从数据库取参数会如何?
 
假设每个客户端通过一个 AdoStoredProcduce (存储过程)来获取参数,我们可以在此存储过程中,
加入数据库的 sp_getapplock (以 MSSQL为例)来让客户端按顺序获取到参数。更新亦同理。
 
由此可见,学习了多线程的锁,亦能推而广之理解数据库的锁。
 
以下是详细代码,至此,本教程的任务基本完成。
本例的调用方法就不写了。如果你读懂了所有的教程,应该会写了。
如果没读懂,写了又有什么意义呢?请恶补面向对象的基础知识。
以后的其它教程,均以本多线程教程为基础。
 
unit uJooThread;
interface
uses
  Classes, SyncObjs, uFooThread, uFooList;
type
  PSyncRec = ^TSyncRec;
  TSyncRec = record
    Method: TThreadMethod; // 这是类的方法
    Proc: TThreadProcedure; // 这是匿名方法
    // 本例只写了类的方法。需要匿名方法,请自行重载 Sync 与 Queue
    Signle: TEvent;
    Queue: boolean;
  end;
 
  TSyncRecList = class(TFooList<PSyncRec>) //用于装执行代码的 List
  protected
    procedure FreeItem(Item: PSyncRec); override;
  end;
 
  TJooThread = class(TFooThread)
  private
    FSyncRecList: TSyncRecList;
    procedure Check;
  public
    constructor Create(ACanAccessCom: boolean);
    destructor Destroy; override;
    procedure Synchronize(AProc: TThreadMethod); // 阻塞到 AProc执行完毕才返回。
    procedure Queue(AProc: TThreadMethod); // 塞入线程后立即返回。
  end;
  // 本例就是前面单节讲的知识的综合运用。
  // TEvent,FooThread,FooList,全都用上了。
  // 并构建了一个新的线程功能。
  // 当我写完以后发现,与系统源码中,
  // 窗口接收 WM_NULL 消息后的处理UI操作的功能,几乎是一模一样的。
  // 不同的是,本例是在线程时空,系统源码是在主线程时空。
implementation
{ TJooThread }
procedure TJooThread.Check;
var
  p: PSyncRec;
begin
  FSyncRecList.Lock; // 所有要执行的代码,都在这个 List 中了。
  // 此处是线程时空,故从List 中取出并执行代码即可。
  try
    p := nil;
    if FSyncRecList.Count > 0 then // 每次取 List 的第一个来执行。
    begin
      p := FSyncRecList[0];
      FSyncRecList.Delete(0);
    end;
  finally
    FSyncRecList.Unlock;
  end;
 
  if Assigned(p) then
  begin
 
    if Assigned(p.Method) then
      p.Method
    else if Assigned(p.Proc) then
      p.Proc();
 
    if not p.Queue then // 如果是阻塞,就置信号。
      p.Signle.SetEvent;
 
    Dispose(p);
 
    ExecProcInThread(Check);
  end;
end;
 
constructor TJooThread.Create(ACanAccessCom: boolean);
begin
  inherited;
  FSyncRecList := TSyncRecList.Create;
end;
 
destructor TJooThread.Destroy;
begin
  FSyncRecList.Free;
  inherited;
end;
 
procedure TJooThread.Queue(AProc: TThreadMethod);
var
  p: PSyncRec;
begin
  FSyncRecList.Lock;
  try
    new(p);
    FSyncRecList.Add(p);
    p.Method := AProc;
    p.Queue := true;
    ExecProcInThread(Check);
  finally
    FSyncRecList.Unlock;
  end;
end;
 
procedure TJooThread.Synchronize(AProc: TThreadMethod);
var
  p: PSyncRec;
  o: TEvent;
begin
  FSyncRecList.Lock;
  try
    new(p);
    FSyncRecList.Add(p);
    p.Method := AProc;
    o := TEvent.Create(niltruefalse'');
    p.Signle := o;
    p.Queue := false;
    ExecProcInThread(Check); //触发线程启动
  finally
    FSyncRecList.Unlock;
  end;
  o.WaitFor; // 等待 AProc 执行完毕的信号
  o.Free;
end;
 
{ TSyncRecList }
procedure TSyncRecList.FreeItem(Item: PSyncRec);
begin
  inherited;
  if Assigned(Item.Signle) then
    Item.Signle.Free;
  Dispose(Item);
end;
 
end.
 
 

delphi 线程教学第七节:在多个线程时空中,把各自的代码塞到一个指定的线程时空运行的更多相关文章

  1. delphi 线程教学第五节:多个线程同时执行相同的任务

    第五节:多个线程同时执行相同的任务   1.锁   设,有一个房间 X ,X为全局变量,它有两个函数  X.Lock 与 X.UnLock; 有如下代码:   X.Lock;      访问资源 P; ...

  2. delphi 线程教学第四节:多线程类的改进

    第四节:多线程类的改进   1.需要改进的地方   a) 让线程类结束时不自动释放,以便符合 delphi 的用法.即 FreeOnTerminate:=false; b) 改造 Create 的参数 ...

  3. delphi 线程教学第六节:TList与泛型

    第六节: TList 与泛型   TList 是一个重要的容器,用途广泛,配合泛型,更是如虎添翼. 我们先来改进一下带泛型的 TList 基类,以便以后使用. 本例源码下载(delphi XE8版本) ...

  4. delphi 线程教学第二节:在线程时空中操作界面(UI)

    第二节:在线程时空中操作界面(UI)   1.为什么要用 TThread ?   TThread 基于操作系统的线程函数封装,隐藏了诸多繁琐的细节. 适合于大部分情况多线程任务的实现.这个理由足够了吧 ...

  5. delphi 线程教学第一节:初识多线程

    第一节:初识多线程   1.为什么要学习多线程编程?   多线程(多个线程同时运行)编程,亦可称之为异步编程. 有了多线程,主界面才不会因为耗时代码而造成“假死“状态. 有了多线程,才能使多个任务同时 ...

  6. delphi 线程教学第三节:设计一个有生命力的工作线程

    第三节:设计一个有生命力的工作线程   创建一个线程,用完即扔.相信很多初学者都曾这样使用过. 频繁创建释放线程,会浪费大量资源的,不科学.   1.如何让多线程能多次被复用?   关键是不让代码退出 ...

  7. CUDA:Supercomputing for the Masses (用于大量数据的超级计算)-第七节

    第七节:使用下一代CUDA硬件,快乐加速度 原文链接 Rob Farber 是西北太平洋国家实验室(Pacific Northwest National Laboratory)的高级科研人员.他在多个 ...

  8. 基于Extjs的web表单设计器 第七节——取数公式设计之取数公式的使用

    基于Extjs的web表单设计器 基于Extjs的web表单设计器 第一节 基于Extjs的web表单设计器 第二节——表单控件设计 基于Extjs的web表单设计器 第三节——控件拖放 基于Extj ...

  9. JAVA 从GC日志分析堆内存 第七节

    JAVA 从GC日志分析堆内存 第七节   在上一章中,我们只设置了整个堆的内存大小.但是我们知道,堆又分为了新生代,年老代.他们之间的内存怎么分配呢?新生代又分为Eden和Survivor,他们的比 ...

随机推荐

  1. maven环境变量的配置及+eclipse的配置使用

    1. 环境搭建(Maven+eclipse) 进入CMD 输入: mvn  –v   查看是否配置好 输入: mvn  -version 可以查看其安装的版本 在eclipse中配置maven: 在h ...

  2. Hibernate HQL中的子查询

    子查询是SQL语句中非常重要的功能特性,它可以在SQL语句中利用另外一条SQL语句的查询结果,在Hibernate中HQL查询同样对子查询功能提供了支持.   如下面代码所示: List list=s ...

  3. python网络编程基础(一)

    一.C/S架构 客户端/服务端架构 二.OSI七层架构 七层模型,亦称OSI(Open System Interconnection)参考模型,是参考模型是国际标准化组织(ISO)制定的一个用于计算机 ...

  4. Hadoop MR编程

    Hadoop开发job需要定一个Map/Reduce/Job(启动MR job,并传入参数信息),以下代码示例实现的功能: 1)将一个用逗号分割的文件,替换为“|”分割的文件: 2)对小文件合并,将文 ...

  5. vue+iview实现动态路由和权限验证

    github上关于vue动态添加路由的例子很多,本项目参考了部分项目后,在iview框架基础上完成了动态路由的动态添加和菜单刷新.为了帮助其他需要的朋友,现分享出实现逻辑,欢迎一起交流学习. Gith ...

  6. [LeetCode] Valid Parenthesis String 验证括号字符串

    Given a string containing only three types of characters: '(', ')' and '*', write a function to chec ...

  7. [NOI 2011]阿狸的打字机

    Description 题库链接 给你 \(n\) 个单词, \(m\) 组询问,每组询问形同 \((x,y)\) ,询问 \(x\) 串在 \(y\) 串中出现多少次. \(1\leq n,m\le ...

  8. [SHOI2011]双倍回文

    Description   Input 输入分为两行,第一行为一个整数,表示字符串的长度,第二行有个连续的小写的英文字符,表示字符串的内容. Output 输出文件只有一行,即:输入数据中字符串的最长 ...

  9. bzoj 1076: [SCOI2008]奖励关

    Description 你正在玩你最喜欢的电子游戏,并且刚刚进入一个奖励关.在这个奖励关里,系统将依次随机抛出k次宝物,每次你都可以选择吃或者不吃(必须在抛出下一个宝物之前做出选择,且现在决定不吃的宝 ...

  10. APIO 2014

    练习赛,评测的时候好像出了些问题,最后我拿自己机子测的212/300,第二题负责评测的写的SPJ就判了第一行的答案,不知道有没出什么问题. T1.palindrome 题目大意:给定一个长度为N的字符 ...