C#用Parallel.Invoke方法盡可能并行執(zhí)行提供的每個(gè)線程
盡可能并行執(zhí)行提供的每個(gè)操作。使用Parallel.Invoke 方法。
最簡(jiǎn)單,最簡(jiǎn)潔的將串行的代碼并行化。
一、重載
| Invoke(Action[]) | 盡可能并行執(zhí)行提供的每個(gè)操作。 |
| Invoke(ParallelOptions, Action[]) | 執(zhí)行所提供的每個(gè)操作,而且盡可能并行運(yùn)行,除非用戶取消了操作。 |
二、Invoke(Action[])
盡可能并行執(zhí)行提供的每個(gè)操作。
1.定義
public static void Invoke (params Action[] actions); 參數(shù) actions Action[] 要執(zhí)行的 Action 數(shù)組。 例外 ArgumentNullException actions 參數(shù)為 null。 AggregateException 當(dāng) actions 數(shù)組中的任何操作引發(fā)異常時(shí)引發(fā)的異常。 ArgumentException actions數(shù)組包含 null 個(gè)元素。
2.示例
Invoke方法經(jīng)常與其他方法、匿名委托和 lambda 表達(dá)式配合使用,實(shí)現(xiàn)并行方法的線程同步。
// Parallel.Invoke()方法
// 將 Invoke 方法與其他方法、匿名委托和 lambda 表達(dá)式結(jié)合使用。
namespace ConsoleApp15
{
class ParallelInvokeDemo
{
/// <summary>
/// 執(zhí)行每個(gè)任務(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);
}
);
}
// 一般不會(huì)出現(xiàn)異常,但如萬(wà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í)行所提供的每個(gè)操作,而且盡可能并行運(yùn)行,除非用戶取消了操作。
1.定義
public static void Invoke (System.Threading.Tasks.ParallelOptions parallelOptions, params Action[] actions); 參數(shù) parallelOptions ParallelOptions 一個(gè)對(duì)象,用于配置此操作的行為。 actions Action[] 要執(zhí)行的操作數(shù)組。 例外 OperationCanceledException CancellationToken 處于 parallelOptions 設(shè)置。 ArgumentNullException actions 參數(shù)為 null。 或 - parallelOptions 參數(shù)為 null。 AggregateException 當(dāng) actions 數(shù)組中的任何操作引發(fā)異常時(shí)引發(fā)的異常。 ArgumentException actions數(shù)組包含 null 個(gè)元素。 ObjectDisposedException 在 parallelOptions 中與 CancellationTokenSource 關(guān)聯(lián)的 CancellationToken 已被釋放。 注解 此方法可用于執(zhí)行一組可能并行的操作。 使用結(jié)構(gòu)傳入 ParallelOptions 的取消令牌使調(diào)用方能夠取消整個(gè)操作。
2. 常用的使用方法
Parallel.Invoke(
() => { },
() => { },
() => { }
);(1)示例1
- 一個(gè)任務(wù)是可以分解成多個(gè)任務(wù),采用分而治之的思想;
- 盡可能的避免子任務(wù)之間的依賴性,因?yàn)樽尤蝿?wù)是并行執(zhí)行,所以就沒有誰(shuí)一定在前,誰(shuí)一定在后的規(guī)定了;
- 主線程必須等Invoke中的所有方法執(zhí)行完成后返回才繼續(xù)向下執(zhí)行。暗示以后設(shè)計(jì)并行的時(shí)候,要考慮每個(gè)Task任務(wù)盡可能差不多,如果相差很大,比如一個(gè)時(shí)間非常長(zhǎng),其他都比較短,這樣一個(gè)線程可能會(huì)影響整個(gè)任務(wù)的性能。這點(diǎn)非常重要;
- 沒有固定的順序,每個(gè)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é)束,共用時(shí){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é)束,共用時(shí)5020ms
*/(3)Stopwatch類
提供一組方法和屬性,可用于準(zhǔn)確地測(cè)量運(yùn)行時(shí)間。
其中,Stopwatch.Start 方法和Stopwatch.Stop 方法
public class Stopwatch
使用 Stopwatch 類來確定應(yīng)用程序的執(zhí)行時(shí)間。
// 使用 Stopwatch 類來確定應(yīng)用程序的執(zhí)行時(shí)間
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 類來計(jì)算性能數(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ù)測(cè)量某個(gè)時(shí)間間隔的運(yùn)行時(shí)間。
前例中有示例。
public void Start ();
2.Stopwatch.Stop 方法
停止測(cè)量某個(gè)時(shí)間間隔的運(yùn)行時(shí)間。
public void Stop ();
前例中有示例。
到此這篇關(guān)于C#用Parallel.Invoke方法盡可能并行執(zhí)行提供的每個(gè)線程的文章就介紹到這了,更多相關(guān)C# Parallel.Invoke并行執(zhí)行提供線程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#?將數(shù)據(jù)庫(kù)SqlServer數(shù)據(jù)綁定到類中的過程詳解
本文講述的是讀取數(shù)據(jù)庫(kù)中數(shù)據(jù)的常用做法,即將數(shù)據(jù)庫(kù)中的數(shù)據(jù)綁定到創(chuàng)建的類中,再將類綁定到DataGridView的數(shù)據(jù)源中的做法,對(duì)C#將SqlServer數(shù)據(jù)綁定到類中感興趣的朋友一起看看吧2022-06-06
C#中單問號(hào)(?)和雙問號(hào)(??)的用法整理
本文詳細(xì)講解了C#中單問號(hào)(?)和雙問號(hào)(??)的用法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05
C#解碼base64編碼二進(jìn)制數(shù)據(jù)的方法
這篇文章主要介紹了C#解碼base64編碼二進(jìn)制數(shù)據(jù)的方法,涉及C#中Convert類的靜態(tài)方法Convert.FromBase64String使用技巧,需要的朋友可以參考下2015-04-04
C# JavaScriptSerializer序列化時(shí)的時(shí)間處理詳解
這篇文章主要為大家詳細(xì)介紹了C# JavaScriptSerializer序列化時(shí)的時(shí)間處理詳解,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
WebService 的簡(jiǎn)單封裝接口調(diào)用方法
這篇文章主要介紹了WebService 的簡(jiǎn)單封裝接口調(diào)用方法,主要是通過簡(jiǎn)單的sql語(yǔ)句來查詢數(shù)據(jù)庫(kù),從而返回dataset,十分簡(jiǎn)單實(shí)用,有需要的小伙伴可以參考下。2015-06-06

