C#用Parallel.Invoke方法尽可能并行执行提供的每个线程
作者:wenchm
本文主要介绍了C#用Parallel.Invoke方法尽可能并行执行提供的每个线程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
尽可能并行执行提供的每个操作。使用Parallel.Invoke 方法。
最简单,最简洁的将串行的代码并行化。
一、重载
Invoke(Action[]) | 尽可能并行执行提供的每个操作。 |
Invoke(ParallelOptions, Action[]) | 执行所提供的每个操作,而且尽可能并行运行,除非用户取消了操作。 |
二、Invoke(Action[])
尽可能并行执行提供的每个操作。
1.定义
public static void Invoke (params Action[] actions); 参数 actions Action[] 要执行的 Action 数组。 例外 ArgumentNullException actions 参数为 null。 AggregateException 当 actions 数组中的任何操作引发异常时引发的异常。 ArgumentException actions数组包含 null 个元素。
2.示例
Invoke方法经常与其他方法、匿名委托和 lambda 表达式配合使用,实现并行方法的线程同步。
// Parallel.Invoke()方法 // 将 Invoke 方法与其他方法、匿名委托和 lambda 表达式结合使用。 namespace ConsoleApp15 { class ParallelInvokeDemo { /// <summary> /// 执行每个任务的线程可能不同。 /// 不同的执行中线程分配可能不同。 /// 任务可能按任何顺序执行。 /// </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); } ); } // 一般不会出现异常,但如万一抛出异常, // 它将被包装在 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); } } } // 运行结果: /* Method=beta, Thread=4 Method=alpha, Thread=1 Method=gamma, Thread=10 */
三、Invoke(ParallelOptions, Action[])
执行所提供的每个操作,而且尽可能并行运行,除非用户取消了操作。
1.定义
public static void Invoke (System.Threading.Tasks.ParallelOptions parallelOptions, params Action[] actions); 参数 parallelOptions ParallelOptions 一个对象,用于配置此操作的行为。 actions Action[] 要执行的操作数组。 例外 OperationCanceledException CancellationToken 处于 parallelOptions 设置。 ArgumentNullException actions 参数为 null。 或 - parallelOptions 参数为 null。 AggregateException 当 actions 数组中的任何操作引发异常时引发的异常。 ArgumentException actions数组包含 null 个元素。 ObjectDisposedException 在 parallelOptions 中与 CancellationTokenSource 关联的 CancellationToken 已被释放。 注解 此方法可用于执行一组可能并行的操作。 使用结构传入 ParallelOptions 的取消令牌使调用方能够取消整个操作。
2. 常用的使用方法
Parallel.Invoke( () => { }, () => { }, () => { } );
(1)示例1
- 一个任务是可以分解成多个任务,采用分而治之的思想;
- 尽可能的避免子任务之间的依赖性,因为子任务是并行执行,所以就没有谁一定在前,谁一定在后的规定了;
- 主线程必须等Invoke中的所有方法执行完成后返回才继续向下执行。暗示以后设计并行的时候,要考虑每个Task任务尽可能差不多,如果相差很大,比如一个时间非常长,其他都比较短,这样一个线程可能会影响整个任务的性能。这点非常重要;
- 没有固定的顺序,每个Task可能是不同的线程去执行,也可能是相同的;
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("我是任务一,我跑了3s"); Thread.Sleep(3000); } static void Run2() { Console.WriteLine("我是任务二,我跑了5s"); Thread.Sleep(5000); } } } //运行结果: /* 我是任务二,我跑了5s 我是任务一,我跑了3s */
(2)示例2
如果调用的方法是有参数的,如何处理?同理,直接带上参数就可以,
Parallel.Invoke(() => Task1("task1"), () => Task2("task2"), () => Task3("task3"));
// Invoke带参数 调用 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};结束,共用时{2}ms", "Main", Environment.CurrentManagedThreadId, stopWatch.ElapsedMilliseconds); Console.ReadKey(); } private static void Task1(string data) { Thread.Sleep(5000); Console.WriteLine("任务名:{0}线程ID : {1}", data, Environment.CurrentManagedThreadId); } private static void Task2(string data) { Console.WriteLine("任务名:{0}线程ID : {1}", data, Environment.CurrentManagedThreadId); } private static void Task3(string data) { Console.WriteLine("任务名:{0}线程ID : {1}", data, Environment.CurrentManagedThreadId); } } } //运行结果: /* 主线程:Main线程ID : 1;开始 任务名:task2线程ID : 4 任务名:task3线程ID : 7 任务名:task1线程ID : 1 主线程:Main线程ID : 1;结束,共用时5020ms */
(3)Stopwatch类
提供一组方法和属性,可用于准确地测量运行时间。
其中,Stopwatch.Start 方法和Stopwatch.Stop 方法
public class Stopwatch
使用 Stopwatch 类来确定应用程序的执行时间。
// 使用 Stopwatch 类来确定应用程序的执行时间 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); } } //运行结果: /* RunTime 00:00:10.01 */
// 使用 Stopwatch 类来计算性能数据。 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); } } } } //运行结果: /* 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 方法
开始或继续测量某个时间间隔的运行时间。
前例中有示例。
public void Start ();
2.Stopwatch.Stop 方法
停止测量某个时间间隔的运行时间。
public void Stop ();
前例中有示例。
到此这篇关于C#用Parallel.Invoke方法尽可能并行执行提供的每个线程的文章就介绍到这了,更多相关C# Parallel.Invoke并行执行提供线程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!