您的位置:首页 > 编程语言 > Delphi

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

2017-03-23 08:08 513 查看
传统的编译型语言对多线程访问同一公共变量都会先锁定,旧版的Delphi亦如是,如果不先锁定,多半会有地址操作错误之类的运行时提示.
  但XE的出现让一切都免了,固定内存占用的数据类型(integer,double,long之类)同时读写没事, 即使变长的String类型亦没事!! 除了公共变量, 连对象的属性都一样,下面是测试代码:

[delphi]
view plain
copy

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也一样没事!

[delphi]
view plain
copy

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>,如下:

[delphi]
view plain
copy

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 地址出错 .
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息