使用Interlocked在多線程下進行原子操作,無鎖無阻塞的實現線程運行狀態判斷

来源:https://www.cnblogs.com/zhiyong-ITNote/archive/2018/01/25/8352848.html
-Advertisement-
Play Games

巧妙地使用Interlocked的各個方法,再無鎖無阻塞的情況下判斷出所有線程的運行完成狀態。 昨晚耐著性子看完了clr via c#的第29章<<基元線程同步構造>>,儘管這本書不是第一次看了,但是之前看的都是一帶而過,沒有深入理解,甚至可以說是不理解,實習了之後發現自己的知識原來這麼錶面,很多的 ...


巧妙地使用Interlocked的各個方法,再無鎖無阻塞的情況下判斷出所有線程的運行完成狀態。

昨晚耐著性子看完了clr via c#的第29章<<基元線程同步構造>>,儘管這本書不是第一次看了,但是之前看的都是一帶而過,沒有深入理解,甚至可以說是不理解,實習了之後發現自己的知識原來這麼錶面,很多的實現都不能做出來,這很大程度上打擊了我,而且,春招也快來了,更需要打扎實基礎。引起我註意的是jeffrey在第29章說的:使用Interlocked,代碼很短,絕不阻塞任何線程,二期使用線程池線程來實現自動伸縮。下載了源碼,然後分析了下書中的示例,code如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace vlr_via_cs
{
    internal static class AsyncCoordinatorDemo
    {
        public static void Go()
        {
            const Int32 timeout = 50000;   // Change to desired timeout
            MultiWebRequests act = new MultiWebRequests(timeout);
            Console.WriteLine("All operations initiated (Timeout={0}). Hit <Enter> to cancel.",
               (timeout == Timeout.Infinite) ? "Infinite" : (timeout.ToString() + "ms"));
            Console.ReadLine();
            act.Cancel();

            Console.WriteLine();
            Console.WriteLine("Hit enter to terminate.");
            Console.ReadLine();
        }

        private sealed class MultiWebRequests
        {
            // This helper class coordinates all the asynchronous operations
            private AsyncCoordinator m_ac = new AsyncCoordinator();

            // Set of Web servers we want to query & their responses (Exception or Int32)
            private Dictionary<String, Object> m_servers = new Dictionary<String, Object> {
                { "http://cjjjs.com/", null },
                { "http://cnblogs.com/", null },
                { "http://www.jobbole.com/", null }
            };

            public MultiWebRequests(Int32 timeout = Timeout.Infinite)
            {
                // Asynchronously initiate all the requests all at once
                var httpClient = new HttpClient();
                foreach (var server in m_servers.Keys)
                {
                    m_ac.AboutToBegin(1); //確保先做三次加法, 若是有Sleep,在調用完這個函數後,執行
                    httpClient.GetByteArrayAsync(server).ContinueWith(task => ComputeResult(server, task));
                }

                // Tell AsyncCoordinator that all operations have been initiated and to call
                // AllDone when all operations complete, Cancel is called, or the timeout occurs
                m_ac.AllBegun(AllDone, timeout);
            }

            private void ComputeResult(String server, Task<Byte[]> task)
            {
                Object result;
                if (task.Exception != null)
                {
                    result = task.Exception.InnerException;
                }
                else
                {
                    // Process I/O completion here on thread pool thread(s)
                    // Put your own compute-intensive algorithm here...
                    result = task.Result.Length;   // This example just returns the length
                }

                // Save result (exception/sum) and indicate that 1 operation completed
                m_servers[server] = result;
                m_ac.JustEnded();
            }

            // Calling this method indicates that the results don't matter anymore
            public void Cancel() { m_ac.Cancel(); }

            // This method is called after all Web servers respond, 
            // Cancel is called, or the timeout occurs
            private void AllDone(CoordinationStatus status)
            {
                switch (status)
                {
                    case CoordinationStatus.Cancel:
                        Console.WriteLine("Operation canceled.");
                        break;

                    case CoordinationStatus.Timeout:
                        Console.WriteLine("Operation timed-out.");
                        break;

                    case CoordinationStatus.AllDone:
                        Console.WriteLine("Operation completed; results below:");
                        foreach (var server in m_servers)
                        {
                            Console.Write("{0} ", server.Key);
                            Object result = server.Value;
                            if (result is Exception)
                            {
                                Console.WriteLine("failed due to {0}.", result.GetType().Name);
                            }
                            else
                            {
                                Console.WriteLine("returned {0:N0} bytes.", result);
                            }
                        }
                        break;
                }
            }
        }

        private enum CoordinationStatus
        {
            AllDone,
            Timeout,
            Cancel
        };

        private sealed class AsyncCoordinator
        {
            private Int32 m_opCount = 1;        // Decremented when AllBegun calls JustEnded
            private Int32 m_statusReported = 0; // 0=false, 1=true
            private Action<CoordinationStatus> m_callback;
            private Timer m_timer;

            // This method MUST be called BEFORE initiating an operation
            public void AboutToBegin(Int32 opsToAdd = 1)
            {
                Interlocked.Add(ref m_opCount, opsToAdd);
            }

            // This method MUST be called AFTER an operations result has been processed
            public void JustEnded()
            {
                if (Interlocked.Decrement(ref m_opCount) == 0)
                    ReportStatus(CoordinationStatus.AllDone);
            }

            // This method MUST be called AFTER initiating ALL operations
            public void AllBegun(Action<CoordinationStatus> callback, Int32 timeout = Timeout.Infinite)
            {
                m_callback = callback;
                if (timeout != Timeout.Infinite)
                {
                    // 在指定的時間點(dueTime) 調用回調函數,隨後在指定的時間間隔(period)調用回調函數
                    m_timer = new Timer(TimeExpired, null, timeout, Timeout.Infinite);
                }
                JustEnded();
            }

            // 處理過時的線程
            private void TimeExpired(Object o) {
                ReportStatus(CoordinationStatus.Timeout);
            }

            public void Cancel()
            {
                if (m_callback == null)
                    throw new InvalidOperationException("Cancel cannot be called before AllBegun");
                ReportStatus(CoordinationStatus.Cancel);
            }

            private void ReportStatus(CoordinationStatus status)
            {
                if (m_timer != null)
                {  // If timer is still in play, kill it
                    Timer timer = Interlocked.Exchange(ref m_timer, null);
                    if (timer != null) timer.Dispose();
                }

                // If status has never been reported, report it; else ignore it
                if (Interlocked.Exchange(ref m_statusReported, 1) == 0)
                    m_callback(status);
            }
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            AsyncCoordinatorDemo.Go();

            Console.Read();
        }
    }
}

的確是無鎖的操作,Interlocked方法是用戶模式下的原子操作,針對的是CPU,不是線程記憶體,而且它是自旋等待的,耗費的是CPU資源。分析了下AsyncCoordinator類,主要就是利用Interlocked的Add方法,實時計數線程的數量,隨後待一個線程運行的最後又調用Interlocked的Decrement方法自減。如果你留心的話,你會發現,目前絕大多數的併發判斷中都用到了Interlocked的這些方法,尤其是interlocked的anything模式下的compareexchange方法,在這裡提一嘴,除了compareexchange和exchange方法的返回值是返回ref類型原先的值之外,其餘的方法都是返回改變之後的值。最後我們可以通過AllBegun方法來判斷是不是所有的線程都執行完了,隨後將狀態變數m_statusReported設置為1,防止在進行狀態判斷。

這個類很好,之前寫併發的時候,老是煩惱怎麼判斷併發是否已經完事了,又不想用到阻塞,這個類很好,當然應用到具體項目中可能還需要改,但是基本的模型還是這個,不變的。

有點感慨:好東西需要我們自己去發掘,之前查生產者消費者模型的時候,java代碼一大堆,愣是沒有看到幾個C#,就算有也是簡易,儘管可以把java的改變為C#的,但有點感慨C#的技術棧和資源少


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 消費方初始化 消費方初始化的入口在ReferenceConfig類的get方法 前面基本和服務方的初始化一致 創建代理類,代理遠程方法實現 ...
  • 1、bool值 這個貌似還可以理解,有比較運算符,邏輯運算符都是產生bool的值,先瞭解這幾個。中學數學題有這樣的真假(想起數學的痛)。 有一個int函數。百度解釋是取整的。先不去刻意瞭解,怕鑽進死衚衕。 2、if 看到判斷真假還有if,還真是一起數學題的一道題目。需要註意的就是老是直接寫數字,而不 ...
  • 服務方初始化 服務方初始化的入口在ServiceConfig類的export方法 這裡的初始化是針對一個服務的 首先判斷是否要發佈,以及延遲發佈 先處理各種配置,按優先順序覆蓋 從配置中取註冊中心URL,註冊中心可能有多個 ...
  • 1.衡量演算法的標準 演算法 解題的方法和步驟 衡量演算法的標準 1.時間複雜度 大概程式要執行的次數,而非執行的時間,不同的機器運行時間肯定不一樣。 2.空間複雜度 演算法執行過程中大概所占用的最大記憶體 3.難易程度 易於理解,便於閱讀 4.健壯性 2.數據結構的地位 數據結構是軟體中最核心的課程 程式= ...
  • public static void main(String[] args) { //虛擬機調用main函數,需要傳個args的參數,傳入的是new String[0] System.out.println(args);// 結果是一個數組實體,長度是0 } 主函數的定義:(主函數也是一個函數) p ...
  • 從類似如下的文本文件中讀取出所有的姓名,並列印出重覆的姓名和重覆的次數,並按重覆次數排序 1,張三,28 2,李四,35 3,張三,28 4,王五,35 5,張三,28 6,李四,35 7,趙六,28 8,田七,35 上面代碼讀取的是docx的word文件,直接讀取會出現亂碼,因word中不僅有文本 ...
  • 1.數據結構概述 定義 我們如何把現實中大量而複雜的問題以特定的數據類型和特定的存儲結構保存到主存儲器(記憶體)中, 以及在此基礎上為實現某個功能(比如查找、刪除、查找、某個元素,對所有元素進行排序)而執行的相應操作 這個相應的操作也叫演算法 數據結構=個體+個體關係 演算法=對存儲數據的操作 預備知識 ...
  • 序列 1、根據列表、元組、字元串的共同點把它們統稱為序列(他們都是兄弟呀) 1)都可以通過索引來的到每一個元素 2)預設索引值都是從零開始(Python也支持負數索引) 3)都可以通過分片(切片)的方式得到新的字元串 4)都有相同的操作符(重覆操作符、拼接操作符、成員關係操作符) 2、與之相關的內置 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...