DelphiXE10.2.3实现线程安全访问数据和对象(一)——Delphi原子操作函数介绍

     之前写《原子自旋锁》时,也是第一次研究原子操作方法,看着测试结果出来时,自己兴奋了很久,后来在实际改造项目时,由于需要解决单链表、HASH、对象池这三种常用数据存储方式,索性一鼓作气将这三种一起实现了,相关源代码会放到CSDN上,这里先将Delphi原子操作函数介绍清楚,以便对源码进行改造时,能够避免问题的发生。

一、Delphi的原子操作函数

    在System.SyncObjs单元中,有一个TInterlocked的密封类,其十多个类函数(class function)其实都是调用的System单元的原子操作函数,只是封装得更容易理解。

   使用方法:如对一个数值加一,则直接b:= TInterlocked.Increment(a);或TInterlocked.Increment(a);,不用创建TInterlocked类(类函数相当于就是单独的函数,可以直接调用,不用实例化对象)

 1、对一个数值变量加一和减一操作

//实际是调用System单元的AtomicIncrement函数

  //对Target加一,并返回加一后的结果

    class function Increment(var Target: Integer): Integer; overload; static; inline;

    class function Increment(var Target: Int64): Int64; overload; static; inline;

  //对Target减一,并返回减一后的结果

    class function Decrement(var Target: Integer): Integer; overload; static; inline;

    class function Decrement(var Target: Int64): Int64; overload; static; inline;

实例一: 

 var

   a,b:integer;

 begin

   a:=1;

   b:= TInterlocked.Increment(a);

   //则b=2(等于a加一后的结果),同时a=2(a=1+1),而这时的a加一是原子操作,不会受多线程影响,能够保证数据一致性。

 end;

2、对一个数值、指针、对象地址、泛型类的地址进行替换操作

  //将Target替换为Value,并返回原来的Target值

 //实际是调用System单元的AtomicExchange函数

    class function Exchange(var Target: Pointer; Value: Pointer): Pointer; overload; static; inline;
    class function Exchange(var Target: Integer; Value: Integer): Integer; overload; static; inline;
    class function Exchange(var Target: Int64; Value: Int64): Int64; overload; static; inline;
    class function Exchange(var Target: TObject; Value: TObject): TObject; overload; static; inline;
    class function Exchange(var Target: Double; Value: Double): Double; overload; static; inline;
    class function Exchange(var Target: Single; Value: Single): Single; overload; static; inline;

    class function Exchange<T: class>(var Target: T; Value: T): T; overload; static; inline;

实例二: 

 var

   a,b,c:integer;

 begin

   a:=1;

   b:=2;

   c:= TInterlocked.Exchange(a,b);

   //则c=1(等于a之前的值),同时a=2(a被替换成b的值),而这时的a是原子操作,不会受多线程影响,能够保证数据一致性。

 end;

3、比较两个值,相同时,用一个值替换掉目标值

//比较Target与Comparand两个值,当两个值相等,则将Target的值替换为Value的值,这个过程非常重要,是实现原子锁的核心

//实际是调用System单元的AtomicCmpExchange函数

class function CompareExchange(var Target: Pointer; Value: Pointer; Comparand: Pointer): Pointer; overload; static; inline;
    class function CompareExchange(var Target: Integer; Value: Integer; Comparand: Integer): Integer; overload; static; inline;
    class function CompareExchange(var Target: Integer; Value: Integer; Comparand: Integer; out Succeeded: Boolean): Integer; overload; static;
    class function CompareExchange(var Target: Int64; Value: Int64; Comparand: Int64): Int64; overload; static; inline;
    class function CompareExchange(var Target: TObject; Value: TObject; Comparand: TObject): TObject; overload; static; inline;
    class function CompareExchange(var Target: Double; Value: Double; Comparand: Double): Double; overload; static; inline;
    class function CompareExchange(var Target: Single; Value: Single; Comparand: Single): Single; overload; static; inline;

    class function CompareExchange<T: class>(var Target: T; Value: T; Comparand: T): T; overload; static; inline;

这里要分成比较值相等和不相等两种情况

实例三: 

 var

   a,b,c,d,e:integer;

 begin

   a:=1;

   b:=2;

   c:=3;

   e:=1;

  //比较a与c的值是否相等,相等则用b的值替换掉a的值,不相等则不替换

  //由于a等于e(都等于1),则a的值被替换成b的值,即a=2,同时d等于a原来的值,即d=1

  d:=TInterlocked.CompareExchange(a,b,e);

 //由于a不等于c的值,所以a不会被替换,则d=1(等于a的值)

   d:= TInterlocked.CompareExchange(a,b,c);

  //注意了,不管a是否等于c和e的值,返回的一定是a原来的值,这个特性非常容易让我们判断a值到底是多少。

 end;


4、用原子操作性读取一个数值

//如果在32位多线程中直接读取一个64位变量,寄存器是分为高字节和低字节两个部分,会读到脏数据,用原子操作读取则不会

class function Read(var Target: Int64): Int64; static; inline;

实际是调用System单元的AtomicExchange函数,如:b:=AtomicExchange(a,0,0);,始终会取到a原来的值,所以并不限于int64类型。


QQ群:DELPHI开发者群:733975324


猜你喜欢

转载自blog.csdn.net/u011784006/article/details/80493118