【delphi 线程】用TMonitor同步共享资源

前言

TMonitor 是用于同步线程的一个记录。说清楚点,我们讨论的是Delphi 的 System.TMonitor 单元 , 而不是 Vcl.Forms.TMonitor 单元。 

自Delphi 2009年以来,TObject实例的大小增加了一倍,以便增加额外的4个字节。这4个字节是干什么的?事实上就是提供TMonitor支持!现在每个继承自TObject的对象都可以使用TMonitor锁。TMonitor记录,它实现了一个通用的监视器同步的结构。

 

源代码下载 

 

 

准备

在这个文章中,我们将讨论一个经典的多线程访问共享文件的并发问题。具体的就是,我们有很多线程同时访问一个共享文件,所有的线程必须进行同步。如果不进行同步,将会导致程序出现共享错误,解决这个线程同步问题也有很多方法,无疑TMonitor是最简单的方法。开始吧...

如何做?

按照如下步骤使用TMonitor同步共享资源。

  1. 创建一个新的 VCL 程序(菜单 File | New | VCL Forms Application);
  2. 在主Form上放置一个TButton, TListBox, 和 TTimer 控件;
  3. 命名TButton为 btnStart,标题更改为:写入共享文件;
  4. 在项目中增加一个单元,命名为: FileWriterThreadU.pas,同时写入如下代码:
unit FileWriterThreadU;
interface
uses
  System.Classes, System.SyncObjs, System.SysUtils, System.IOUtils;
type
  TThreadHelper = class helper for TThread
public
  function WaitFor(ATimeout: Cardinal): LongWord; platform;
end;
TFileWriterThread = class(TThread)
private
  FStreamWriter: TStreamWriter;
protected
  procedure Execute; override;
public
  constructor Create(AStreamWriter: TStreamWriter);
end;
implementation
{$IF Defined(MSWINDOWS)}
uses
  Winapi.Windows;
{$IFEND}
constructor TFileWriterThread.Create(AStreamWriter: TStreamWriter);
begin
  FStreamWriter := AStreamWriter;
  inherited Create(False);
end;
procedure TFileWriterThread.Execute;
var
  I: Integer;
  NumLines: Integer;
begin
  inherited;
  NumLines := 11 + Random(50);
  for I := 1 to NumLines do
    begin
      TThread.Sleep(200);
      //here we are locking the shared resource
      TMonitor.Enter(FStreamWriter);
      try
        FStreamWriter.WriteLine(Format('THREAD %5d - ROW %2d',
        [TThread.CurrentThread.ThreadID, I]));
      finally
        //unlock the shared resource
        TMonitor.Exit(FStreamWriter);
      end;
      if Terminated then
         Break;
    end;
end;
function TThreadHelper.WaitFor(ATimeout: Cardinal): LongWord;
begin
{$IF Defined(MSWINDOWS)}
  Result := WaitForSingleObject(Handle, ATimeout);
{$ELSE}
  raise Exception.Create('Available only on MS Windows');
{$IFEND}
end;
initialization
Randomize; // we'll use Random function in the thread
end.

        5. 返回主From单元,在接口部分增加如下引用:

  • System.Generics.Collections
  • FileWriterThreadU

        6. 在 From 的private节增加如下变量:

                private
                        FOutputFile: TStreamWriter;
                        FRunningThreads: TObjectList<TFileWriterThread>

        7. 在 From的  FormCreate 和 FormClose事件中增加如下代码:

                procedure TMainForm.FormCreate(Sender: TObject);
                begin
                        FRunningThreads := TObjectList<TFileWriterThread>.Create;        
                        FOutputFile := TStreamWriter.Create(TFileStream.Create('OutputFile.txt',
                                fmCreate or fmShareDenyWrite));
                end;
                procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
                var
                        Th: TFileWriterThread;
                begin
                         for Th in FRunningThreads do
                                Th.Terminate;
                                FRunningThreads.Free; // Implicit WaitFor...
                                FOutputFile.Free;
                 end;

        

                前面的代码,创建了一个数据结构来保存线程列表和文件访问。FOutputFile变量是所有线程访问的共享资源。

        8. 给 btnStart 按键的 Click 事件增加如下代码:

                procedure TMainForm.btnStartClick(Sender: TObject);
                var
                        I: Integer;
                        Th: TFileWriterThread;
                begin
                        for I := 1 to 10 do
                                begin
                                        Th := TFileWriterThread.Create(FOutputFile);
                                        FRunningThreads.Add(Th);
                                end;
                end;

        以上代码创建10个线程,共享文件 FOutputFile

        9. 现在程序已经可以没有问题的跑起来了,但是在UI层面,我们并不清楚每个线程的情况,所以增加一个定时器,用来显示线程的情况。定时器检查线程列表,并将线程的状态显示在 ListBox1 中。

        procedure TMainForm.Timer1Timer(Sender: TObject);
        var
                Th: TFileWriterThread;
        begin
                ListBox1.Items.BeginUpdate;
                try
                        ListBox1.Items.Clear;
                        for Th in FRunningThreads do
                        begin
                                if Th.WaitFor(0) = WAIT_TIMEOUT then
                                        ListBox1.Items.Add(Format('%5d RUNNING', [Th.ThreadID]))
                                else
                                        ListBox1.Items.Add(Format('%5d TERMINATED', [Th.ThreadID]))
                        end;
                finally
                        ListBox1.Items.EndUpdate;
                end;
        end;

        10. 运行程序,按下 btnStart 按键,结果界面:

 

        11. 程序运行结束后,我们可以检查共享文件,没有任何错误发生,数据也没有丢失,线程共享运行良好。

        

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Delphi是一种用于编写Windows平台应用程序的开发工具,而SQL Server是一种关系型数据库管理系统。在Delphi中实现多线程读写SQL Server数据库有以下几个步骤: 1. 引入相关单元:首先,在Delphi中需要引入一些相关的单元,如ADODB、ComObj等,以便使用数据库访问组件和相关的COM对象。 2. 连接数据库:在多线程处理之前,需要先建立与SQL Server数据库的连接。可以使用ADODB组件中的TADOConnection来连接数据库,并设置相关的连接字符串。 3. 创建多个线程:在Delphi中创建多线程可以使用TThread类,可以派生出自定义的线程类,并在Execute方法中实现具体的数据库操作。每个线程负责自己的数据库读写操作。 4. 执行SQL语句:在每个线程的Execute方法中,可以使用TADOQuery或TADOCommand等组件来执行SQL语句。可以使用Prepare方法预编译SQL语句,然后使用Parameters设置参数值,最后通过Execute或Open方法执行SQL语句。 5. 处理数据:在读取数据库数据时,可以使用TADOQuery组件的Recordset属性来获取返回的记录集,然后使用相关的方法或属性获取数据,并进行相应的处理。 6. 线程同步:在多线程操作数据库时,需要注意线程同步问题。可以使用TMonitor类或TCriticalSection类来实现线程间的互斥访问,以避免操作冲突和数据不一致。 7. 关闭连接:在所有线程的操作都完成后,需要关闭与数据库的连接,释放相关资源和线程。 总之,通过在Delphi线程中使用ADODB组件和相关组件,可以实现对SQL Server数据库的读写操作。但需要注意线程同步和资源管理,以避免出现问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海纳老吴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值