关于新版Delphi的并发操作及公共变量并发读写

本文探讨了Delphi XE中多线程并发访问同一变量时的稳定性表现,通过实验发现对于固定内存占用的数据类型,即使没有手动锁定也能正常工作。然而,对于复杂的数据结构,如TDictionary,则会出现地址操作错误。

        传统的编译型语言对多线程访问同一公共变量都会先锁定,旧版的Delphi亦如是,如果不先锁定,多半会有地址操作错误之类的运行时提示.

  但XE的出现让一切都免了,固定内存占用的数据类型(integer,double,long之类)同时读写没事, 即使变长的String类型亦没事!! 除了公共变量, 连对象的属性都一样,下面是测试代码:

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
    Lck: TCriticalSection;
    tasks: array of ITask;
    fv: String;
  public
    { Public declarations }
    property v:String read fv write fv;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  i: integer;
begin
  SetLength(tasks, 150);
  for i := Low(tasks) to High(tasks) do
  begin
    tasks[i] := TTask.Create(
      procedure()
      var
        i, j: integer;
      begin
        for i := 1 to 500000000 do
        begin
          Form1.v := inttostr(i); // 并发给全局V赋值,但不会出错?
          j := strtoint(Form1.v); // 并发读取v的值
          if j <> i then
          begin
            // OutputDebugString(pchar(format('%d,%d',[j,i])));
          end;
          if TTaskStatus.Canceled = TTask.CurrentTask.Status then
          begin
            break;
          end;
          sleep(random(5));
        end;
        OutputDebugString('Thread Finished');
      end);
    tasks[i].Start;
  end;
end;

      

改为用TThread也一样没事!

threads:array of tthread;
procedure TForm1.Button3Click(Sender: TObject);
var
  i: integer;
begin
   tag:=0;
  SetLength(threads,150);
  for i := 0 to 149 do
  begin
    threads[i] := TThread.CreateAnonymousThread(
      procedure()
      var
        i, j: integer;
      begin
        OutputDebugString(pchar('Thread '+INTTOSTR(tthread.CurrentThread.Handle)+' started.'));
        for i := 1 to 500000000 do
        begin
          Form1.v := inttostr(i); // 并发给全局V赋值,但不会出错?
          j := strtoint(Form1.v); // 并发读取v的值
          if j <> i then
          begin
            // OutputDebugString(pchar(format('%d,%d',[j,i])));
          end;
          if form1.Tag<>0 then
          begin
            break;
          end;
          sleep(random(5));
        end;
        OutputDebugString('Thread Finished');
      end);
    threads[i].FreeOnTerminate := true;
    threads[i].Start;
  end;
end;


为什么那么神奇?? 二小姐的回复说 ismulthread是System单元里的一个开关,表示是否运行在多线程模式中,多线程模式下fastmm会给分配内存之类的操作用原子指令来加锁,用Task和TThread之类不用设置这个变量,它们内部本身就有设置. 不得不给XE点100个赞.


但是问题来了, 是不是所有数据类型都能自动处理?? 并不是,若将fv改为TDictionary<integer,String>,如下:

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
  private
    { Private declarations }
    Lck: TCriticalSection;
    tasks: array of ITask;
    threads:array of tthread;
    fv:TDictionary<integer,String>;
  public
    { Public declarations }
    property v: TDictionary<integer,String> read fv write fv;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  i: integer;
begin
  SetLength(tasks, 150);
  for i := Low(tasks) to High(tasks) do
  begin
    tasks[i] := TTask.Create(
      procedure()
      var
        i, j: integer;
      begin
        for i := 1 to 500000000 do
        begin
          Form1.v.AddOrSetValue(i,inttostr(i)); // 并发给全局V赋值,但不会出错?
          j := strtoint(Form1.v.Items[i]); // 并发读取v的值
          if j <> i then
          begin
            // OutputDebugString(pchar(format('%d,%d',[j,i])));
          end;
          if TTaskStatus.Canceled = TTask.CurrentTask.Status then
          begin
            break;
          end;
          sleep(random(5));
        end;
        OutputDebugString('Thread Finished');
      end);
    tasks[i].Start;
  end;
end;

因为TDictionary是个比较复杂的类,存取里面的代码复杂, fastmm内置的原子锁就无能为力了, 运行一会就出现write 地址出错 .


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值