.NET 實現並行的幾種方式(二)

来源:http://www.cnblogs.com/08shiyan/archive/2016/08/19/5787860.html
-Advertisement-
Play Games

本隨筆續接:.NET 實現並行的幾種方式(一) 四、Task 3)Task.NET 4.5 中的簡易方式 在上篇隨筆中,兩個Demo使用的是 .NET 4.0 中的方式,代碼寫起來略顯麻煩,這不 .NET 4.5提供了更加簡潔的方式,讓我們來看一下吧。 /// <summary> /// Task. ...


本隨筆續接:.NET 實現並行的幾種方式(一) 

四、Task  

3)Task.NET 4.5 中的簡易方式

在上篇隨筆中,兩個Demo使用的是 .NET 4.0 中的方式,代碼寫起來略顯麻煩,這不 .NET 4.5提供了更加簡潔的方式,讓我們來看一下吧。

        /// <summary>
        /// Task.NET 4.5 中的簡易方式
        /// </summary>
        public void Demo3()
        {
            Task.Run(() =>
            {
                SetTip("簡潔的代碼");
            });


            Task.Run(() =>
            {
                SetTip("驗證 CreationOptions 屬性");
            }).ContinueWith((t)=> {
                SetTip("CreationOptions:" + t.CreationOptions.ToString());                
            });
        }
Task.NET 4.5 中的簡易方式

 

五、TPL (Task Parallel Library)

TPL (任務並行庫)是 .NET 4.0 中的另一個重量級模塊,可以極其優雅、便捷地完成並行邏輯的編碼工作。

1)Parallel.Invoke並行多個獨立的Action

        /// <summary>
        /// Parallel.Invoke並行多個獨立的Action
        /// </summary>
        public void Demo1()
        {
            Task.Run(() =>
            {
                List<Action> actions = new List<Action>();

                // 生成並行任務
                for (int i = 0; i < 5; i++)
                {
                    // 註意、這裡很關鍵,不可直接使用i變數。 
                    // 原因在稍後的隨筆中進行說明
                    int index = i;
                    actions.Add(new Action(() =>
                    {
                        SetTip(string.Format("Task{0} 開始", index));

                        SetTip(string.Format("Task{0} 休眠1秒", index));
                        Thread.Sleep(1000);


                        SetTip(string.Format("Task{0} 休眠5秒", index));
                        Thread.Sleep(5000);
                        
                        SetTip(string.Format("Task{0} 結束", index));
                    }));
                }

                // 執行並行任務
                Parallel.Invoke(actions.ToArray());

                // 當上述的5個任務全部執行完畢後,才會執行該代碼
                SetTip("並行任務執行完畢");
            });
        }
Parallel.Invoke並行多個獨立的Action

 

2)Parallel簡單的For並行

如果 Parallel.Invoke 看做是任務並行, 則 Parallel.For 則是數據並行,可方便的完成For迴圈並行遍歷。

        /// <summary>
        /// Parallel簡單的For並行
        /// </summary>
        public void Demo2()
        {
            // 為了實時更新UI、將代碼非同步執行
            Task.Run(() =>
            {
                Parallel.For(1, 100, (index) =>
                {
                    SetTip(string.Format("Index:{0}, 開始執行Task", index));

                    Thread.Sleep(1000);
                    SetTip(string.Format("Index:{0}, 開始休眠Action 1秒", index));

                    SetTip(string.Format("Index:{0}, Task執行完畢", index));
                });

                SetTip("並行任務執行完畢");
            });
        }
Parallel簡單的For並行

 

3)Parallel.For並行 並行中的 break、 return、 continue

break : 在 Parallel.For 中使用 ParallelLoopState.Break() 方法代替。

return: 在 Parallel.For 中使用 ParallelLoopState.Break() 方法代替。

continue : 在 Parallel.For 中直接使用 return 即可。

        /// <summary>
        /// 中斷Parallel.For並行
        /// </summary>
        public void Demo3()
        {
            // 為了實時更新UI、將代碼非同步執行
            Task.Run(() =>
            {
                int breakIndex = new Random().Next(10, 50);
                SetTip(" BreakIndex : -------------------------" + breakIndex);

                Parallel.For(1, 100, (index, state) =>
                {
                    SetTip(string.Format("Index:{0}, 開始執行Task", index));

                    if (breakIndex == index)
                    {
                        SetTip(string.Format("Index:{0}, ------------------ Break Task", index));
                        state.Break();
                        // Break方法執行後、
                        // 大於 當前索引的並且未被安排執行的迭代將被放棄
                        // 小於 當前索引的的迭代將繼續正常執行直至迭代執行完畢
                        return;
                    }

                    Thread.Sleep(1000);
                    SetTip(string.Format("Index:{0}, 休眠Action 1秒", index));

                    SetTip(string.Format("Index:{0}, Task執行完畢", index));
                });

                SetTip("並行任務執行完畢");
            });
        }


        /// <summary>
        /// 終止Parallel.For並行
        /// </summary>
        public void Demo4()
        {
            // 為了實時更新UI、將代碼非同步執行
            Task.Run(() =>
            {
                int stopIndex = new Random().Next(10, 50);
                SetTip(" StopIndex : -------------------------" + stopIndex);

                Parallel.For(1, 100, (index, state) =>
                {
                    SetTip(string.Format("Index:{0}, 開始執行Task", index));

                    if (stopIndex == index)
                    {
                        SetTip(string.Format("Index:{0}, ------------------ Stop Task", index));
                        state.Stop();
                        // Stop方法執行後
                        // 整個迭代將被放棄
                        return;
                    }

                    Thread.Sleep(1000);
                    SetTip(string.Format("Index:{0}, 休眠Action 1秒", index));

                    SetTip(string.Format("Index:{0}, Task執行完畢", index));
                });

                SetTip("並行任務執行完畢");
            });
        }
Parallel.For並行 並行中的 break、 return、 continue

 

4)Parallel.For並行中的數據聚合

在並行中,絕大多數委托都是在不同的線程中運行的,如果需要在並行中進行的數據共用、則需要考慮線程同步問題,然而線程同步會影響並行性能。

為瞭解決特定情況下的數據共用,而又不會因為線程同步而影響性能,Parallel.For 提供瞭解決方案:

        /// <summary>
        /// Parallel.For並行中的數據聚合
        /// </summary>
        public void Demo5()
        {
            Task.Run(() =>
            {
                // 求 1 到 10 的階乘的 和
                long total = 0;
                Parallel.For<long>(1, 10,
                    () =>
                    {
                        SetTip("LocalInit");
                        return 0;
                    },
                    (index, state, local) =>
                    {
                        SetTip("Body");
                        int result = 1;
                        for (int i = 2; i < index; i++)
                        {
                            result *= i;
                        }
                        local += result;
                        return local;
                    },
                    (x) =>
                    {
                        SetTip("LocalFinally");
                        Interlocked.Add(ref total, x);
                    });

                SetTip("Total : " + total);
                SetTip("並行任務執行完畢");
            });

        }
Parallel.For並行中的數據聚合

MSDN備註:
對於參與迴圈執行的每個線程調用 LocalInit 委托一次,並返回每個線程的初始本地狀態。
這些初始狀態傳遞到每個線程上的第一個 body 調用。 然後,每個後續正文調用返回可能修改過的狀態值,傳遞到下一個正文調用。
最後,每個線程上的最後正文調用返回傳遞給 LocalFinally 委托的狀態值。
每個線程調用 localFinally 委托一次,以對每個線程的本地狀態執行最終操作。
此委托可以被多個線程同步調用;因此您必須同步對任何共用變數的訪問。

也就是說:
1) 並行中開闢的線程數 決定了 LocalInit、LocalFinally 的調用次數
2) 多個 迭代委托、Body 可能被同一個線程調用。
3) 迭代委托、Body 中的 local值,並不一定是 LocalInit 的初始值,也有可能是被修改的返回值。
4) LocalFinally 可能是被同時調用的,需要註意線程同步問題。

 

5)Parallel.ForEach並行

Parallel.ForEach算是另一種數據並行方式, 它與大家熟知的 IEnumerable<TSource> 介面結合十分緊密,是 foreach的並行版本。

        /// <summary>
        /// Parallel.ForEach並行
        /// </summary>
        public void Demo6()
        {
            Task.Run(() =>
            {
                Parallel.ForEach<int>(Enumerable.Range(1, 10), (num) =>
                {
                    SetTip("Task 開始");


                    SetTip("Task 休眠" + num + "");
                    Thread.Sleep(TimeSpan.FromSeconds(num));


                    SetTip("Task 結束");
                });

                SetTip("並行任務執行完畢");
            });
        }
Parallel.ForEach並行

 

6)Parallel.ForEach中的索引,中斷、終止操作

在 Parallel.ForEach 中也可以輕易的獲得其遍歷的索引

        /// <summary>
        /// Parallel.ForEach中的索引,中斷、終止操作
        /// </summary>
        public void Demo7()
        {
            Task.Run(() =>
            {
                Parallel.ForEach<int>(Enumerable.Range(0, 10), (num, state, index) =>
                {
                    // num, 並行數據源中的數據項
                    // state, 
                    SetTip(" Index : " + index + "         Num: " + num);
                });
                SetTip("並行任務執行完畢");
            });
        }
Parallel.ForEach中的索引,中斷、終止操作

 

 

本隨筆到此、暫告一段落。

附,Demo : http://files.cnblogs.com/files/08shiyan/ParallelDemo.zip

 

參見更多:隨筆導讀:同步與非同步


(未完待續...)


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

-Advertisement-
Play Games
更多相關文章
  • 監控作業發現一臺伺服器(Red Hat Enterprise Linux Server release 5.7)從凌晨1:32開始,有一小段時間無法響應,資料庫也連接不上,後面又正常了。早上檢查了監聽日誌,並沒有發現錯誤信息。但是檢查告警日誌,發現有下麵錯誤信息: Thread 1 advanced... ...
  • 資源表是一個樹形結構,可以設置成2的31次方的層數,Windows 使用了3級: 類型->名稱->語言 其中涉及到四個結構: Data Description Resource Directory Tables (and Resource Directory Entries) A series of... ...
  • 介紹 LINQ(目錄) 語言集成查詢 (LINQ) 是 Visual Studio 2008 和 .NET Framework 3.5 版中引入的一項創新功能。 傳統上,針對數據的查詢都是以簡單的字元串表示,而沒有編譯時類型檢查或 IntelliSense 支持。 此外,您還必須針對以下各種數據源學 ...
  • 開始使用 LINQ (一)- 介紹 LINQ 查詢 查詢是一種從數據源檢索數據的表達式。 隨著時間的推移,人們已經為各種數據源開發了不同的語言;例如,用於關係資料庫的 SQL 和用於 XML 的 XQuery。 因此,開發人員不得不針對他們必須支持的每種數據源或數據格式而學習新的查詢語言。 LINQ ...
  • 開始使用 LINQ (二)- 基本 LINQ 查詢操作 一、獲取數據源:from 在 LINQ 查詢中,第一步是指定數據源。像在大多數編程語言中一樣,在 C# 中,必須先聲明變數,才能使用它。在 LINQ 查詢中,最先使用 from 子句的目的是引入數據源 (customers) 和範圍變數 (cu ...
  • 開始使用 LINQ(三)- 使用 LINQ 進行數據轉換 語言集成查詢 (LINQ) 不僅可用於檢索數據, 而且還是一個功能強大的數據轉換工具。 通過使用 LINQ 查詢,您可以將源序列用作輸入,並採用多種方式修改它以創建新的輸出序列。您可以通過排序和分組來修改該序列,而不必修改元素本身。但是,LI ...
  • 補充下上篇文章集合的命令。 上篇地址 博客園蝸牛 http://www.cnblogs.com/tdws/p/5785939.html SCARD Key獲得執行集合中元素的數量。 SDIFFSTORE 目標鍵 keys ... 將集合做DIFF差集操作,並存儲在目標鍵中。 另外同樣有SINTERS ...
  • 通常情況下我們在使用IBatisNet的時候,配置文件和映射文件都是暴露在外的,如果能進入到伺服器,那麼你的程式的操作資料庫的SQL語句,資料庫連接字元串等信息都將很輕鬆的被看到,這樣是很危險的。然而IBatisnet自身也沒有提供配置文件直接加密的方法,但我們可以用變通的方式來儘可能的保護這些文件 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...