C#用Parallel.Invoke方法盡可能并行執(zhí)行提供的每個線程
盡可能并行執(zhí)行提供的每個操作。使用Parallel.Invoke 方法。
最簡單,最簡潔的將串行的代碼并行化。
一、重載
Invoke(Action[]) | 盡可能并行執(zhí)行提供的每個操作。 |
Invoke(ParallelOptions, Action[]) | 執(zhí)行所提供的每個操作,而且盡可能并行運(yùn)行,除非用戶取消了操作。 |
二、Invoke(Action[])
盡可能并行執(zhí)行提供的每個操作。
1.定義
public static void Invoke (params Action[] actions); 參數(shù) actions Action[] 要執(zhí)行的 Action 數(shù)組。 例外 ArgumentNullException actions 參數(shù)為 null。 AggregateException 當(dāng) actions 數(shù)組中的任何操作引發(fā)異常時引發(fā)的異常。 ArgumentException actions數(shù)組包含 null 個元素。
2.示例
Invoke方法經(jīng)常與其他方法、匿名委托和 lambda 表達(dá)式配合使用,實現(xiàn)并行方法的線程同步。
// Parallel.Invoke()方法 // 將 Invoke 方法與其他方法、匿名委托和 lambda 表達(dá)式結(jié)合使用。 namespace ConsoleApp15 { class ParallelInvokeDemo { /// <summary> /// 執(zhí)行每個任務(wù)的線程可能不同。 /// 不同的執(zhí)行中線程分配可能不同。 /// 任務(wù)可能按任何順序執(zhí)行。 /// </summary> static void Main() { try { Parallel.Invoke( BasicAction, // Param #0 - static method () => // Param #1 - lambda expression { Console.WriteLine("Method=beta, Thread={0}", Environment.CurrentManagedThreadId); }, delegate () // Param #2 - in-line delegate { Console.WriteLine("Method=gamma, Thread={0}", Environment.CurrentManagedThreadId); } ); } // 一般不會出現(xiàn)異常,但如萬一拋出異常, // 它將被包裝在 AggregateException 中并傳播到主線程。 catch (AggregateException e) { Console.WriteLine("An action has thrown an exception. THIS WAS UNEXPECTED.\n{0}", e.InnerException!.ToString()); } } static void BasicAction() { Console.WriteLine("Method=alpha, Thread={0}", Environment.CurrentManagedThreadId); } } } // 運(yùn)行結(jié)果: /* Method=beta, Thread=4 Method=alpha, Thread=1 Method=gamma, Thread=10 */
三、Invoke(ParallelOptions, Action[])
執(zhí)行所提供的每個操作,而且盡可能并行運(yùn)行,除非用戶取消了操作。
1.定義
public static void Invoke (System.Threading.Tasks.ParallelOptions parallelOptions, params Action[] actions); 參數(shù) parallelOptions ParallelOptions 一個對象,用于配置此操作的行為。 actions Action[] 要執(zhí)行的操作數(shù)組。 例外 OperationCanceledException CancellationToken 處于 parallelOptions 設(shè)置。 ArgumentNullException actions 參數(shù)為 null。 或 - parallelOptions 參數(shù)為 null。 AggregateException 當(dāng) actions 數(shù)組中的任何操作引發(fā)異常時引發(fā)的異常。 ArgumentException actions數(shù)組包含 null 個元素。 ObjectDisposedException 在 parallelOptions 中與 CancellationTokenSource 關(guān)聯(lián)的 CancellationToken 已被釋放。 注解 此方法可用于執(zhí)行一組可能并行的操作。 使用結(jié)構(gòu)傳入 ParallelOptions 的取消令牌使調(diào)用方能夠取消整個操作。
2. 常用的使用方法
Parallel.Invoke( () => { }, () => { }, () => { } );
(1)示例1
- 一個任務(wù)是可以分解成多個任務(wù),采用分而治之的思想;
- 盡可能的避免子任務(wù)之間的依賴性,因為子任務(wù)是并行執(zhí)行,所以就沒有誰一定在前,誰一定在后的規(guī)定了;
- 主線程必須等Invoke中的所有方法執(zhí)行完成后返回才繼續(xù)向下執(zhí)行。暗示以后設(shè)計并行的時候,要考慮每個Task任務(wù)盡可能差不多,如果相差很大,比如一個時間非常長,其他都比較短,這樣一個線程可能會影響整個任務(wù)的性能。這點非常重要;
- 沒有固定的順序,每個Task可能是不同的線程去執(zhí)行,也可能是相同的;
namespace ConsoleApp16 { internal class Program { private static void Main(string[] args) { ArgumentNullException.ThrowIfNull(args); ParallelMothed(); static void ParallelMothed() { Parallel.Invoke(Run1, Run2); //這里的Run1 Run2 都是方法。 } } static void Run1() { Console.WriteLine("我是任務(wù)一,我跑了3s"); Thread.Sleep(3000); } static void Run2() { Console.WriteLine("我是任務(wù)二,我跑了5s"); Thread.Sleep(5000); } } } //運(yùn)行結(jié)果: /* 我是任務(wù)二,我跑了5s 我是任務(wù)一,我跑了3s */
(2)示例2
如果調(diào)用的方法是有參數(shù)的,如何處理?同理,直接帶上參數(shù)就可以,
Parallel.Invoke(() => Task1("task1"), () => Task2("task2"), () => Task3("task3"));
// Invoke帶參數(shù) 調(diào)用 using System.Diagnostics; namespace ConsoleApp17 { class ParallelInvoke { public static void Main(string[] args) { ArgumentNullException.ThrowIfNull(args); Stopwatch stopWatch = new(); Console.WriteLine("主線程:{0}線程ID : {1};開始", "Main", Environment.CurrentManagedThreadId); stopWatch.Start(); Parallel.Invoke( () => Task1("task1"), () => Task2("task2"), () => Task3("task3") ); stopWatch.Stop(); Console.WriteLine("主線程:{0}線程ID : {1};結(jié)束,共用時{2}ms", "Main", Environment.CurrentManagedThreadId, stopWatch.ElapsedMilliseconds); Console.ReadKey(); } private static void Task1(string data) { Thread.Sleep(5000); Console.WriteLine("任務(wù)名:{0}線程ID : {1}", data, Environment.CurrentManagedThreadId); } private static void Task2(string data) { Console.WriteLine("任務(wù)名:{0}線程ID : {1}", data, Environment.CurrentManagedThreadId); } private static void Task3(string data) { Console.WriteLine("任務(wù)名:{0}線程ID : {1}", data, Environment.CurrentManagedThreadId); } } } //運(yùn)行結(jié)果: /* 主線程:Main線程ID : 1;開始 任務(wù)名:task2線程ID : 4 任務(wù)名:task3線程ID : 7 任務(wù)名:task1線程ID : 1 主線程:Main線程ID : 1;結(jié)束,共用時5020ms */
(3)Stopwatch類
提供一組方法和屬性,可用于準(zhǔn)確地測量運(yùn)行時間。
其中,Stopwatch.Start 方法和Stopwatch.Stop 方法
public class Stopwatch
使用 Stopwatch 類來確定應(yīng)用程序的執(zhí)行時間。
// 使用 Stopwatch 類來確定應(yīng)用程序的執(zhí)行時間 using System.Diagnostics; class Program { static void Main(string[] args) { ArgumentNullException.ThrowIfNull(args); Stopwatch stopWatch = new(); stopWatch.Start(); Thread.Sleep(10000); stopWatch.Stop(); // Get the elapsed time as a TimeSpan value. TimeSpan ts = stopWatch.Elapsed; // Format and display the TimeSpan value. string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10); Console.WriteLine("RunTime " + elapsedTime); } } //運(yùn)行結(jié)果: /* RunTime 00:00:10.01 */
// 使用 Stopwatch 類來計算性能數(shù)據(jù)。 using System.Diagnostics; namespace StopWatchSample { class OperationsTimer { public static void Main(string[] args) { ArgumentNullException.ThrowIfNull(args); DisplayTimerProperties(); Console.WriteLine(); Console.WriteLine("Press the Enter key to begin:"); Console.ReadLine(); Console.WriteLine(); TimeOperations(); } public static void DisplayTimerProperties() { // Display the timer frequency and resolution. if (Stopwatch.IsHighResolution) { Console.WriteLine("Operations timed using the system's high-resolution performance counter."); } else { Console.WriteLine("Operations timed using the DateTime class."); } long frequency = Stopwatch.Frequency; Console.WriteLine(" Timer frequency in ticks per second = {0}",frequency); long nanosecPerTick = (1000L * 1000L * 1000L) / frequency; Console.WriteLine(" Timer is accurate within {0} nanoseconds",nanosecPerTick); } private static void TimeOperations() { long nanosecPerTick = (1000L * 1000L * 1000L) / Stopwatch.Frequency; const long numIterations = 10000; // Define the operation title names. string[] operationNames = {"Operation: Int32.Parse(\"0\")", "Operation: Int32.TryParse(\"0\")", "Operation: Int32.Parse(\"a\")", "Operation: Int32.TryParse(\"a\")"}; // Time four different implementations for parsing // an integer from a string. for (int operation = 0; operation <= 3; operation++) { // Define variables for operation statistics. long numTicks = 0; long numRollovers = 0; long maxTicks = 0; long minTicks = long.MaxValue; int indexFastest = -1; int indexSlowest = -1; Stopwatch time10kOperations = Stopwatch.StartNew(); // Run the current operation 10001 times. // The first execution time will be tossed // out, since it can skew the average time. for (int i = 0; i <= numIterations; i++) { long ticksThisTime = 0; int inputNum; Stopwatch timePerParse; switch (operation) { case 0: // Parse a valid integer using // a try-catch statement. // Start a new stopwatch timer. timePerParse = Stopwatch.StartNew(); try { inputNum = int.Parse("0"); } catch (FormatException) { inputNum = 0; } // Stop the timer, and save the // elapsed ticks for the operation. timePerParse.Stop(); ticksThisTime = timePerParse.ElapsedTicks; break; case 1: // Parse a valid integer using // the TryParse statement. // Start a new stopwatch timer. timePerParse = Stopwatch.StartNew(); if (!int.TryParse("0", out inputNum)) { inputNum = 0; } // Stop the timer, and save the // elapsed ticks for the operation. timePerParse.Stop(); ticksThisTime = timePerParse.ElapsedTicks; break; case 2: // Parse an invalid value using // a try-catch statement. // Start a new stopwatch timer. timePerParse = Stopwatch.StartNew(); try { inputNum = int.Parse("a"); } catch (FormatException) { inputNum = 0; } // Stop the timer, and save the // elapsed ticks for the operation. timePerParse.Stop(); ticksThisTime = timePerParse.ElapsedTicks; break; case 3: // Parse an invalid value using // the TryParse statement. // Start a new stopwatch timer. timePerParse = Stopwatch.StartNew(); if (!int.TryParse("a", out inputNum)) { inputNum = 0; } // Stop the timer, and save the // elapsed ticks for the operation. timePerParse.Stop(); ticksThisTime = timePerParse.ElapsedTicks; break; default: break; } // Skip over the time for the first operation, // just in case it caused a one-time // performance hit. if (i == 0) { time10kOperations.Reset(); time10kOperations.Start(); } else { // Update operation statistics // for iterations 1-10000. if (maxTicks < ticksThisTime) { indexSlowest = i; maxTicks = ticksThisTime; } if (minTicks > ticksThisTime) { indexFastest = i; minTicks = ticksThisTime; } numTicks += ticksThisTime; if (numTicks < ticksThisTime) { // Keep track of rollovers. numRollovers++; } } } // Display the statistics for 10000 iterations. time10kOperations.Stop(); long milliSec = time10kOperations.ElapsedMilliseconds; Console.WriteLine(); Console.WriteLine("{0} Summary:", operationNames[operation]); Console.WriteLine(" Slowest time: #{0}/{1} = {2} ticks", indexSlowest, numIterations, maxTicks); Console.WriteLine(" Fastest time: #{0}/{1} = {2} ticks", indexFastest, numIterations, minTicks); Console.WriteLine(" Average time: {0} ticks = {1} nanoseconds", numTicks / numIterations, (numTicks * nanosecPerTick) / numIterations); Console.WriteLine(" Total time looping through {0} operations: {1} milliseconds", numIterations, milliSec); } } } } //運(yùn)行結(jié)果: /* Operations timed using the system's high-resolution performance counter. Timer frequency in ticks per second = 10000000 Timer is accurate within 100 nanoseconds */
1.Stopwatch.Start 方法
開始或繼續(xù)測量某個時間間隔的運(yùn)行時間。
前例中有示例。
public void Start ();
2.Stopwatch.Stop 方法
停止測量某個時間間隔的運(yùn)行時間。
public void Stop ();
前例中有示例。
到此這篇關(guān)于C#用Parallel.Invoke方法盡可能并行執(zhí)行提供的每個線程的文章就介紹到這了,更多相關(guān)C# Parallel.Invoke并行執(zhí)行提供線程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#?將數(shù)據(jù)庫SqlServer數(shù)據(jù)綁定到類中的過程詳解
本文講述的是讀取數(shù)據(jù)庫中數(shù)據(jù)的常用做法,即將數(shù)據(jù)庫中的數(shù)據(jù)綁定到創(chuàng)建的類中,再將類綁定到DataGridView的數(shù)據(jù)源中的做法,對C#將SqlServer數(shù)據(jù)綁定到類中感興趣的朋友一起看看吧2022-06-06C#解碼base64編碼二進(jìn)制數(shù)據(jù)的方法
這篇文章主要介紹了C#解碼base64編碼二進(jìn)制數(shù)據(jù)的方法,涉及C#中Convert類的靜態(tài)方法Convert.FromBase64String使用技巧,需要的朋友可以參考下2015-04-04C# JavaScriptSerializer序列化時的時間處理詳解
這篇文章主要為大家詳細(xì)介紹了C# JavaScriptSerializer序列化時的時間處理詳解,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08