最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺談C#在網(wǎng)絡(luò)波動(dòng)時(shí)防重復(fù)提交的方法

 更新時(shí)間:2019年04月14日 10:31:57   作者:ToolGood  
這篇文章主要介紹了淺談C#在網(wǎng)絡(luò)波動(dòng)時(shí)防重復(fù)提交的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前幾天,公司數(shù)據(jù)庫出現(xiàn)了兩條相同的數(shù)據(jù),而且時(shí)間相同(毫秒也相同)。排查原因,發(fā)現(xiàn)是網(wǎng)絡(luò)波動(dòng)造成了重復(fù)提交。

由于網(wǎng)絡(luò)波動(dòng)而重復(fù)提交的例子也比較多:

 

網(wǎng)絡(luò)上,防重復(fù)提交的方法也很多,使用redis鎖,代碼層面使用lock。

但是,我沒有發(fā)現(xiàn)一個(gè)符合我心意的解決方案。因?yàn)榫W(wǎng)上的解決方案,第一次提交返回成功,第二次提交返回失敗。由于兩次返回信息不一致,一次成功一次失敗,我們不確定客戶端是以哪個(gè)返回信息為準(zhǔn),雖然我們希望客戶端以第一次返回成功的信息為準(zhǔn),但客戶端也可能以第二次失敗信息運(yùn)行,這是一個(gè)不確定的結(jié)果。

在重復(fù)提交后,如果客戶端的接收到的信息都相同,都是成功,那客戶端就可以正常運(yùn)行,就不會(huì)影響用戶體驗(yàn)。

我想到一個(gè)緩存類,來源于PetaPoco。

Cache<TKey, TValue>代碼如下:

public class Cache<TKey, TValue>
  {
    private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
    private readonly Dictionary<TKey, TValue> _map = new Dictionary<TKey, TValue>();

    public int Count {
      get { return _map.Count; }
    }

    public TValue Execute(TKey key, Func<TValue> factory)
    {
      // Check cache
      _lock.EnterReadLock();
      TValue val;
      try {
        if (_map.TryGetValue(key, out val))
          return val;
      } finally {
        _lock.ExitReadLock();
      }

      // Cache it
      _lock.EnterWriteLock();
      try {
        // Check again
        if (_map.TryGetValue(key, out val))
          return val;

        // Create it
        val = factory();

        // Store it
        _map.Add(key, val);

        // Done
        return val;
      } finally {
        _lock.ExitWriteLock();
      }
    }

    public void Clear()
    {
      // Cache it
      _lock.EnterWriteLock();
      try {
        _map.Clear();
      } finally {
        _lock.ExitWriteLock();
      }
    }
  }

Cache<TKey, TValue>符合我的要求,第一次運(yùn)行后,會(huì)將值緩存,第二次提交會(huì)返回第一次的值。

但是,細(xì)細(xì)分析Cache<TKey, TValue> 類,可以發(fā)現(xiàn)有以下幾個(gè)缺點(diǎn)

1、 不會(huì)自動(dòng)清空緩存,適合一些key不多的數(shù)據(jù),不適合做為網(wǎng)絡(luò)接口。

2、 由于_lock.EnterWriteLock,多線程會(huì)變成并單線程,不適合做為網(wǎng)絡(luò)接口。

3、 沒有過期緩存判斷。

于是我對(duì)Cache<TKey, TValue>進(jìn)行改造。

AntiDupCache代碼如下:

/// <summary>
  /// 防重復(fù)緩存
  /// </summary>
  /// <typeparam name="TKey"></typeparam>
  /// <typeparam name="TValue"></typeparam>
  public class AntiDupCache<TKey, TValue>
  {
    private readonly int _maxCount;//緩存最高數(shù)量
    private readonly long _expireTicks;//超時(shí) Ticks
    private long _lastTicks;//最后Ticks
    private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
    private readonly ReaderWriterLockSlim _slimLock = new ReaderWriterLockSlim();
    private readonly Dictionary<TKey, Tuple<long, TValue>> _map = new Dictionary<TKey, Tuple<long, TValue>>();
    private readonly Dictionary<TKey, AntiDupLockSlim> _lockDict = new Dictionary<TKey, AntiDupLockSlim>();
    private readonly Queue<TKey> _queue = new Queue<TKey>();
    class AntiDupLockSlim : ReaderWriterLockSlim { public int UseCount; }

    /// <summary>
    /// 防重復(fù)緩存
    /// </summary>
    /// <param name="maxCount">緩存最高數(shù)量,0 不緩存,-1 緩存所有</param>
    /// <param name="expireSecond">超時(shí)秒數(shù),0 不緩存,-1 永久緩存 </param>
    public AntiDupCache(int maxCount = 100, int expireSecond = 1)
    {
      if (maxCount < 0) {
        _maxCount = -1;
      } else {
        _maxCount = maxCount;
      }
      if (expireSecond < 0) {
        _expireTicks = -1;
      } else {
        _expireTicks = expireSecond * TimeSpan.FromSeconds(1).Ticks;
      }
    }

    /// <summary>
    /// 個(gè)數(shù)
    /// </summary>
    public int Count {
      get { return _map.Count; }
    }

    /// <summary>
    /// 執(zhí)行
    /// </summary>
    /// <param name="key">值</param>
    /// <param name="factory">執(zhí)行方法</param>
    /// <returns></returns>
    public TValue Execute(TKey key, Func<TValue> factory)
    {
      // 過期時(shí)間為0 則不緩存
      if (object.Equals(null, key) || _expireTicks == 0L || _maxCount == 0) { return factory(); }

      Tuple<long, TValue> tuple;
      long lastTicks;
      _lock.EnterReadLock();
      try {
        if (_map.TryGetValue(key, out tuple)) {
          if (_expireTicks == -1) return tuple.Item2;
          if (tuple.Item1 + _expireTicks > DateTime.Now.Ticks) return tuple.Item2;
        }
        lastTicks = _lastTicks;
      } finally { _lock.ExitReadLock(); }


      AntiDupLockSlim slim;
      _slimLock.EnterUpgradeableReadLock();
      try {
        _lock.EnterReadLock();
        try {
          if (_lastTicks != lastTicks) {
            if (_map.TryGetValue(key, out tuple)) {
              if (_expireTicks == -1) return tuple.Item2;
              if (tuple.Item1 + _expireTicks > DateTime.Now.Ticks) return tuple.Item2;
            }
            lastTicks = _lastTicks;
          }
        } finally { _lock.ExitReadLock(); }

        _slimLock.EnterWriteLock();
        try {
          if (_lockDict.TryGetValue(key, out slim) == false) {
            slim = new AntiDupLockSlim();
            _lockDict[key] = slim;
          }
          slim.UseCount++;
        } finally { _slimLock.ExitWriteLock(); }
      } finally { _slimLock.ExitUpgradeableReadLock(); }


      slim.EnterWriteLock();
      try {
        _lock.EnterReadLock();
        try {
          if (_lastTicks != lastTicks && _map.TryGetValue(key, out tuple)) {
            if (_expireTicks == -1) return tuple.Item2;
            if (tuple.Item1 + _expireTicks > DateTime.Now.Ticks) return tuple.Item2;
          }
        } finally { _lock.ExitReadLock(); }

        var val = factory();
        _lock.EnterWriteLock();
        try {
          _lastTicks = DateTime.Now.Ticks;
          _map[key] = Tuple.Create(_lastTicks, val);
          if (_maxCount > 0) {
            if (_queue.Contains(key) == false) {
              _queue.Enqueue(key);
              if (_queue.Count > _maxCount) _map.Remove(_queue.Dequeue());
            }
          }
        } finally { _lock.ExitWriteLock(); }
        return val;
      } finally {
        slim.ExitWriteLock();
        _slimLock.EnterWriteLock();
        try {
          slim.UseCount--;
          if (slim.UseCount == 0) {
            _lockDict.Remove(key);
            slim.Dispose();
          }
        } finally { _slimLock.ExitWriteLock(); }
      }
    }
    /// <summary>
    /// 清空
    /// </summary>
    public void Clear()
    {
      _lock.EnterWriteLock();
      try {
        _map.Clear();
        _queue.Clear();
        _slimLock.EnterWriteLock();
        try {
          _lockDict.Clear();
        } finally {
          _slimLock.ExitWriteLock();
        }
      } finally {
        _lock.ExitWriteLock();
      }
    }

  }

代碼分析:

使用兩個(gè)ReaderWriterLockSlim鎖 + 一個(gè)AntiDupLockSlim鎖,實(shí)現(xiàn)并發(fā)功能。

Dictionary<TKey, Tuple<long, TValue>> _map實(shí)現(xiàn)緩存,long類型值記錄時(shí)間,實(shí)現(xiàn)緩存過期

int _maxCount + Queue<TKey> _queue,_queue 記錄key列隊(duì),當(dāng)數(shù)量大于_maxCount,清除多余緩存。

AntiDupLockSlim繼承ReaderWriterLockSlim,實(shí)現(xiàn)垃圾回收,

代碼使用 :

private readonly static AntiDupCache<int, int> antiDupCache = new AntiDupCache<int, int>(50, 1);

  antiDupCache.Execute(key, () => {

     ....

     return val;

  });

測(cè)試性能數(shù)據(jù):

----------------------- 開始  從1到100   重復(fù)次數(shù):1 單位: ms -----------------------

并發(fā)數(shù)量: 1    2    3    4    5    6    7    8    9    10   11   12

普通并發(fā): 188  93   65   46   38   36   28   31   22   20   18   19

AntiDupCache: 190  97   63   48   37   34   29   30   22   18   17   21

AntiDupQueue: 188  95   63   46   37   33   30   25   21   19   17   21

DictCache: 185  96   64   47   38   33   28   29   22   19   17   21

Cache: 185  186  186  188  188  188  184  179  180  184  184  176

第二次普通并發(fā): 180  92   63   47   38   36   26   28   20   17   16   20

----------------------- 開始  從1到100   重復(fù)次數(shù):2 單位: ms -----------------------

并發(fā)數(shù)量: 1    2    3    4    5    6    7    8    9    10   11   12

普通并發(fā): 368  191  124  93   73   61   55   47   44   37   34   44

AntiDupCache: 180  90   66   48   37   31   28   24   21   17   17   22

AntiDupQueue: 181  93   65   46   39   31   27   23   21   19   18   19

DictCache: 176  97   61   46   38   30   31   23   21   18   18   22

Cache: 183  187  186  182  186  185  184  177  181  177  176  177

第二次普通并發(fā): 366  185  127  95   71   62   56   48   43   38   34   43

----------------------- 開始  從1到100   重復(fù)次數(shù):4 單位: ms -----------------------

并發(fā)數(shù)量: 1    2    3    4    5    6    7    8    9    10   11   12

普通并發(fā): 726  371  253  190  152  132  106  91   86   74   71   69

AntiDupCache: 189  95   64   49   37   33   28   26   22   19   17   18

AntiDupQueue: 184  97   65   51   39   35   28   24   21   18   17   17

DictCache: 182  95   64   45   39   34   29   23   21   18   18   16

Cache: 170  181  180  184  182  183  181  181  176  179  179  178

第二次普通并發(fā): 723  375  250  186  150  129  107  94   87   74   71   67

----------------------- 開始  從1到100   重復(fù)次數(shù):12 單位: ms -----------------------

并發(fā)數(shù)量: 1    2    3    4    5    6    7    8    9    10   11   12

普通并發(fā): 2170 1108 762  569  450  389  325  283  253  228  206  186

AntiDupCache: 182  95   64   51   41   32   28   25   26   20   18   18

AntiDupQueue: 189  93   67   44   37   35   29   30   27   22   20   17

DictCache: 184  97   59   50   38   29   27   26   24   19   18   17

Cache: 174  189  181  184  184  177  182  180  176  176  180  179

第二次普通并發(fā): 2190 1116 753  560  456  377  324  286  249  227  202  189

仿線上環(huán)境,性能測(cè)試數(shù)據(jù):

----------------------- 仿線上環(huán)境  從1到1000  單位: ms -----------------------

并發(fā)數(shù)量: 1    2    3    4    5    6    7    8    9    10   11   12

普通并發(fā): 1852 950  636  480  388  331  280  241  213  198  181  168

AntiDupCache: 1844 949  633  481  382  320  267  239  210  195  174  170

AntiDupQueue: 1835 929  628  479  386  318  272  241  208  194  174  166

DictCache: 1841 935  629  480  378  324  269  241  207  199  176  168

Cache: 1832 1854 1851 1866 1858 1858 1832 1825 1801 1797 1788 1785

第二次普通并發(fā): 1854 943  640  468  389  321  273  237  209  198  177  172

項(xiàng)目:

Github: https://github.com/toolgood/ToolGood.AntiDuplication

Nuget: Install-Package ToolGood.AntiDuplication

后記:

嘗試添加 一個(gè)Queue<AntiDupLockSlim> 或Stack<AntiDupLockSlim> 用來緩存鎖,后發(fā)現(xiàn)性能效率相差不大,上下浮動(dòng)。

使用 lock關(guān)鍵字加鎖,速度相差不大,代碼看似更簡(jiǎn)單,但隱藏了一個(gè)地雷:一般人使用唯一鍵都是使用string,就意味著可能使用lock(string),鎖定字符串尤其危險(xiǎn),因?yàn)樽址还舱Z言運(yùn)行庫 (CLR)“暫留”。 這意味著整個(gè)程序中任何給定字符串都只有一個(gè)實(shí)例,就是這同一個(gè)對(duì)象表示了所有運(yùn)行的應(yīng)用程序域的所有線程中的該文本。因此,只要在應(yīng)用程序進(jìn)程中的任何位置處具有相同內(nèi)容的字符串上放置了鎖,就將鎖定應(yīng)用程序中該字符串的所有實(shí)例。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • c#異常處理示例分享

    c#異常處理示例分享

    這篇文章主要介紹了c#異常處理示例,需要的朋友可以參考下
    2014-04-04
  • 改進(jìn)c# 代碼的五個(gè)技巧(一)

    改進(jìn)c# 代碼的五個(gè)技巧(一)

    這篇文章主要介紹了改進(jìn)c# 代碼的五個(gè)技巧(一),幫助大家更好的理解和使用c#,感興趣的朋友可以了解下
    2021-01-01
  • C#使用checkedListBox1控件鏈接數(shù)據(jù)庫的方法示例

    C#使用checkedListBox1控件鏈接數(shù)據(jù)庫的方法示例

    這篇文章主要介紹了C#使用checkedListBox1控件鏈接數(shù)據(jù)庫的方法,結(jié)合具體實(shí)例形式分析了數(shù)據(jù)庫的創(chuàng)建及checkedListBox1控件連接數(shù)據(jù)庫的相關(guān)操作技巧,需要的朋友可以參考下
    2017-06-06
  • C#實(shí)現(xiàn).net頁面之間傳值傳參方法匯總

    C#實(shí)現(xiàn).net頁面之間傳值傳參方法匯總

    這篇文章主要介紹了C#實(shí)現(xiàn).net頁面之間傳值傳參方法,實(shí)例匯總了幾類常見的傳值傳參的方法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2014-10-10
  • 如何使用C#掃描并讀取圖片中的文字

    如何使用C#掃描并讀取圖片中的文字

    本文介紹如何通過C# 程序來掃描并讀取圖片中的文字,這里以創(chuàng)建一個(gè).Net Core程序?yàn)槔?。下面是具體步驟,供參考。有此需求的朋友可以了解下
    2021-06-06
  • C#簡(jiǎn)單實(shí)現(xiàn)SNMP的方法

    C#簡(jiǎn)單實(shí)現(xiàn)SNMP的方法

    這篇文章主要介紹了C#簡(jiǎn)單實(shí)現(xiàn)SNMP的方法,通過一個(gè)簡(jiǎn)單的自定義類分析了C#實(shí)現(xiàn)SNMP的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-07-07
  • Unity實(shí)現(xiàn)物體運(yùn)動(dòng)軌跡的繪制

    Unity實(shí)現(xiàn)物體運(yùn)動(dòng)軌跡的繪制

    這篇文章主要為大家詳細(xì)介紹了Unity實(shí)現(xiàn)物體運(yùn)動(dòng)軌跡的繪制,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • C#實(shí)現(xiàn)在線更新軟件

    C#實(shí)現(xiàn)在線更新軟件

    winform程序相對(duì)web程序而言,功能更強(qiáng)大,編程更方便,但軟件更新卻相當(dāng)麻煩,要到客戶端一臺(tái)一臺(tái)地升級(jí),面對(duì)這個(gè)實(shí)際問題,在最近的一個(gè)小項(xiàng)目中,本人設(shè)計(jì)了一個(gè)通過軟件實(shí)現(xiàn)自動(dòng)升級(jí)技術(shù)方案,彌補(bǔ)了這一缺陷,有較好的參考價(jià)值
    2015-05-05
  • .Net中的json操作類用法分析

    .Net中的json操作類用法分析

    這篇文章主要介紹了.Net中的json操作類用法分析,是非常實(shí)用的一個(gè)技巧,需要的朋友可以參考下
    2014-08-08
  • Unity3D動(dòng)態(tài)生成平面網(wǎng)格

    Unity3D動(dòng)態(tài)生成平面網(wǎng)格

    這篇文章主要為大家詳細(xì)介紹了Unity3D動(dòng)態(tài)生成平面網(wǎng)格,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-02-02

最新評(píng)論

岳普湖县| 时尚| 白朗县| 涿鹿县| 二连浩特市| 甘孜县| 鹰潭市| 松阳县| 开江县| 沙坪坝区| 灵宝市| 昌邑市| 西和县| 梧州市| 杭锦后旗| 旺苍县| 阿克| 商丘市| 山西省| 普定县| 黔西县| 中山市| 津市市| 英山县| 越西县| 西乌珠穆沁旗| 天祝| 鄯善县| 灵丘县| 翁牛特旗| 板桥市| 高雄县| 广东省| 新龙县| 凤冈县| 文昌市| 吉林市| 阳春市| 乐山市| 达拉特旗| 临潭县|