• Delphi 高效读写锁


    本人设计了一个高效读写锁,可实现多个线程读一个线程写的锁,应该比Delphi自带的读写锁高效,本人没有做对比测试。

    本文的锁不可以在一个线程里重入,否则会锁死,另外读写锁最多支持65535个线程同时读。

    // HeZiHang@cnblogs
    // 跨平台简易高效锁
    
    unit utLocker;
    
    interface
    
    type
      // 多读单写锁
      // 1.写的时候阻塞其他所有写和读
      // 2.读的时候不阻塞其他读,但阻塞所有写,当阻塞了一个或以上的写后,将阻塞所有后来新的读
      TMultiReadSingleWriteLocker = class
      protected
        [Volatile]
        FLocker: Integer;
      public
        procedure LockRead;
        procedure UnLockRead; inline;
        procedure LockWrite;
        procedure UnLockWrite; inline;
        function TryLockRead: Boolean; inline;
        function TryLockWrite: Boolean; inline;
        constructor Create;
      end;
    
      TSimpleLocker = class
      protected
        [Volatile]
        FLocker: Integer;
      public
        procedure Lock;
        procedure UnLock; inline;
        function TryLock: Boolean; inline;
      end;
    
    implementation
    
    uses System.SyncObjs, System.SysUtils, System.Classes;
    
    type
      TSpinWait = record
      private const
        YieldThreshold = 10;
        Sleep1Threshold = 20;
        Sleep0Threshold = 5;
      private
        FCount: Integer;
        function GetNextSpinCycleWillYield: Boolean; inline;
      public
        procedure Reset;inline;
        procedure SpinCycle;inline;
    
        property Count: Integer read FCount;
        property NextSpinCycleWillYield: Boolean read GetNextSpinCycleWillYield;
      end;
    
      { TSpinWait }
    
    function TSpinWait.GetNextSpinCycleWillYield: Boolean;
    begin
      Result := (FCount > YieldThreshold) or (CPUCount = 1);
    end;
    
    procedure TSpinWait.Reset;
    begin
      FCount := 0;
    end;
    
    procedure TSpinWait.SpinCycle;
    var
      SpinCount: Integer;
    begin
      if NextSpinCycleWillYield then
      begin
        if FCount >= YieldThreshold then
          SpinCount := FCount - YieldThreshold
        else
          SpinCount := FCount;
        if SpinCount mod Sleep1Threshold = Sleep1Threshold - 1 then
          TThread.Sleep(1)
        else if SpinCount mod Sleep0Threshold = Sleep0Threshold - 1 then
          TThread.Sleep(0)
        else
          TThread.Yield;
      end
      else
        TThread.SpinWait(4 shl FCount);
      Inc(FCount);
      if FCount < 0 then
        FCount := YieldThreshold + 1;
    end;
    
    { TMultiReadSingleWriteLocker }
    
    procedure TMultiReadSingleWriteLocker.LockRead;
    var
      CurLock: Integer;
      Wait: TSpinWait;
    begin
      Wait.Reset;
      while True do
      begin
        CurLock := FLocker;
        if CurLock <= $FFFF then
        begin
          if TInterlocked.CompareExchange(FLocker, CurLock + 1, CurLock) = CurLock
          then
            Exit;
        end;
        Wait.SpinCycle;
      end;
    end;
    
    procedure TMultiReadSingleWriteLocker.LockWrite;
    var
      CurLock: Integer;
      Wait: TSpinWait;
    begin
      Wait.Reset;
      while True do
      begin
        CurLock := FLocker;
        if CurLock <= $FFFF then
        begin
          if TInterlocked.CompareExchange(FLocker, CurLock + $10000, CurLock) = CurLock
          then
            Exit;
        end;
        Wait.SpinCycle;
      end;
    end;
    
    function TMultiReadSingleWriteLocker.TryLockRead: Boolean;
    var
      CurLock: Integer;
    begin
      CurLock := FLocker;
      if CurLock <= $FFFF then
        Result := TInterlocked.CompareExchange(FLocker, CurLock + 1, CurLock)
          = CurLock
      else
        Result := False;
    end;
    
    function TMultiReadSingleWriteLocker.TryLockWrite: Boolean;
    var
      CurLock: Integer;
    begin
      CurLock := FLocker;
      if CurLock <= $FFFF then
        Result := TInterlocked.CompareExchange(FLocker, CurLock + $10000, CurLock)
          = CurLock
      else
        Result := False;
    end;
    
    procedure TMultiReadSingleWriteLocker.UnLockWrite;
    begin
      if FLocker < $10000 then
        raise Exception.Create('TMultiReadSingleWriteLocker Error');
    
      TInterlocked.Add(FLocker, -$10000);
    end;
    
    procedure TMultiReadSingleWriteLocker.UnLockRead;
    begin
      TInterlocked.Decrement(FLocker);
    end;
    
    constructor TMultiReadSingleWriteLocker.Create;
    begin
      FLocker := 0;
    end;
    
    { TSimpleLocker }
    
    procedure TSimpleLocker.Lock;
    var
      Wait: TSpinWait;
    begin
      Wait.Reset;
      while True do
      begin
        if FLocker = 0 then
        begin
          if TInterlocked.CompareExchange(FLocker, 1, 0) = 0 then
            Exit;
        end;
        Wait.SpinCycle;
      end;
    end;
    
    function TSimpleLocker.TryLock: Boolean;
    begin
      if FLocker = 0 then
      begin
        Result := TInterlocked.CompareExchange(FLocker, 1, 0) = 0;
      end
      else
        Result := False;
    end;
    
    procedure TSimpleLocker.UnLock;
    begin
      if TInterlocked.CompareExchange(FLocker, 0, 1) <> 1 then
        raise Exception.Create('TSimpleLocker Error');
    end;
    
    end.
    

      

  • 相关阅读:
    【心得】软件团队Git工作流及Jira的基本知识和常见问题解释
    项目系统Netty的Channel和用户之间的关系绑定正确做法,以及Channel通道的安全性方案
    Redis中的事务(多命令)操作
    Redis中的订阅模式
    Redis中有序列表(ZSet)相关命令
    Redis散列(Hash)的相关命令
    输入输出流String间的转换
    linux 常用命令
    Gradle 使用
    c 学习笔记 0
  • 原文地址:https://www.cnblogs.com/hezihang/p/5347460.html
Copyright © 2020-2023  润新知