一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Net 4.0并行計(jì)算

 昵稱3945912 2010-10-22
沿用微軟的寫法,System.Threading.Tasks.::.Parallel類,提供對(duì)并行循環(huán)和區(qū)域的支持。 我們會(huì)用到的方法有For,F(xiàn)orEach,Invoke。

一、簡(jiǎn)單使用

        首先我們初始化一個(gè)List用于循環(huán),這里我們循環(huán)10次。(后面的代碼都會(huì)按這個(gè)標(biāo)準(zhǔn)進(jìn)行循環(huán))

Code
  1.             Program.Data = new List<int>();
  2.             for (int i = 0; i < 10; i++)
  3.             {
  4.                 Data.Add(i);
  5.             }

        下面我們定義4個(gè)方法,分別為for,foreach,并行For,并行ForEach。并測(cè)試他們的運(yùn)行時(shí)長(zhǎng)。

Code
  1.         /// <summary>
  2.         /// 是否顯示執(zhí)行過(guò)程
  3.         /// </summary>
  4.         public bool ShowProcessExecution = false;
  5.         /// <summary>
  6.         /// 這是普通循環(huán)for
  7.         /// </summary>
  8.         private void Demo1()
  9.         {
  10.             List<int> data = Program.Data;
  11.             DateTime dt1 = DateTime.Now;
  12.             for (int i = 0; i < data.Count; i++)
  13.             {
  14.                 Thread.Sleep(500);
  15.                 if (ShowProcessExecution)
  16.                     Console.WriteLine(data[i]);
  17.             }
  18.             DateTime dt2 = DateTime.Now;
  19.             Console.WriteLine("普通循環(huán)For運(yùn)行時(shí)長(zhǎng):{0}毫秒。", (dt2 - dt1).TotalMilliseconds);
  20.         }
  21.         /// <summary>
  22.         /// 這是普通循環(huán)foreach
  23.         /// </summary>
  24.         private void Demo2()
  25.         {
  26.             List<int> data = Program.Data;
  27.             DateTime dt1 = DateTime.Now;
  28.             foreach (var i in data)
  29.             {
  30.                 Thread.Sleep(500);
  31.                 if (ShowProcessExecution)
  32.                     Console.WriteLine(i);
  33.             }
  34.             DateTime dt2 = DateTime.Now;
  35.             Console.WriteLine("普通循環(huán)For運(yùn)行時(shí)長(zhǎng):{0}毫秒。", (dt2 - dt1).TotalMilliseconds);
  36.         }
  37.         /// <summary>
  38.         /// 這是并行計(jì)算For
  39.         /// </summary>
  40.         private void Demo3()
  41.         {
  42.             List<int> data = Program.Data;
  43.             DateTime dt1 = DateTime.Now;
  44.             Parallel.For(0, data.Count, (i) =>
  45.             {
  46.                 Thread.Sleep(500);
  47.                 if (ShowProcessExecution)
  48.                     Console.WriteLine(data[i]);
  49.             });
  50.             DateTime dt2 = DateTime.Now;
  51.             Console.WriteLine("并行運(yùn)算For運(yùn)行時(shí)長(zhǎng):{0}毫秒。", (dt2 - dt1).TotalMilliseconds);
  52.         }
  53.         /// <summary>
  54.         /// 這是并行計(jì)算ForEach
  55.         /// </summary>
  56.         private void Demo4()
  57.         {
  58.             List<int> data = Program.Data;
  59.             DateTime dt1 = DateTime.Now;
  60.             Parallel.ForEach(data, (i) =>
  61.             {
  62.                 Thread.Sleep(500);
  63.                 if (ShowProcessExecution)
  64.                     Console.WriteLine(i);
  65.             });
  66.             DateTime dt2 = DateTime.Now;
  67.             Console.WriteLine("并行運(yùn)算ForEach運(yùn)行時(shí)長(zhǎng):{0}毫秒。", (dt2 - dt1).TotalMilliseconds);
  68.         }

下面是運(yùn)行結(jié)果:

image

這里我們可以看出并行循環(huán)在執(zhí)行效率上的優(yōu)勢(shì)了。

結(jié)論1:在對(duì)一個(gè)數(shù)組內(nèi)的每一個(gè)項(xiàng)做單獨(dú)處理時(shí),完全可以選擇并行循環(huán)的方式來(lái)提升執(zhí)行效率。

原理1:并行計(jì)算的線程開啟是緩步開啟的,線程數(shù)量1,2,4,8緩步提升。(不詳,PLinq最多64個(gè)線程,可能這也是64)

  

  

二、 并行循環(huán)的中斷和跳出

        當(dāng)在進(jìn)行循環(huán)時(shí),偶爾會(huì)需要中斷循環(huán)或跳出循環(huán)。下面是兩種跳出循環(huán)的方法Stop和Break,LoopState是循環(huán)狀態(tài)的參數(shù)。

Code
  1.         /// <summary>
  2.         /// 中斷Stop
  3.         /// </summary>
  4.         private void Demo5()
  5.         {
  6.             List<int> data = Program.Data;
  7.             Parallel.For(0, data.Count, (i, LoopState) =>
  8.             {
  9.                 if (data[i] > 5)
  10.                     LoopState.Stop();
  11.                 Thread.Sleep(500);
  12.                 Console.WriteLine(data[i]);
  13.             });
  14.             Console.WriteLine("Stop執(zhí)行結(jié)束。");
  15.         }
  16.         /// <summary>
  17.         /// 中斷Break
  18.         /// </summary>
  19.         private void Demo6()
  20.         {
  21.             List<int> data = Program.Data;
  22.             Parallel.ForEach(data, (i, LoopState) =>
  23.             {
  24.                 if (i > 5)
  25.                     LoopState.Break();
  26.                 Thread.Sleep(500);
  27.                 Console.WriteLine(i);
  28.             });
  29.             Console.WriteLine("Break執(zhí)行結(jié)束。");
  30.         }

        執(zhí)行結(jié)果如下:

image

結(jié)論2:使用Stop會(huì)立即停止循環(huán),使用Break會(huì)執(zhí)行完畢所有符合條件的項(xiàng)。

  

  

三、并行循環(huán)中為數(shù)組/集合添加項(xiàng)

        上面的應(yīng)用場(chǎng)景其實(shí)并不是非常多見,畢竟只是為了遍歷一個(gè)數(shù)組內(nèi)的資源,我們更多的時(shí)候是為了遍歷資源,找到我們所需要的。那么請(qǐng)繼續(xù)看。

下面是我們一般會(huì)想到的寫法:

Code
  1.         private void Demo7()
  2.         {
  3.             List<int> data = new List<int>();
  4.             Parallel.For(0, Program.Data.Count, (i) =>
  5.             {
  6.                 if (Program.Data[i] % 2 == 0)
  7.                     data.Add(Program.Data[i]);
  8.             });
  9.             Console.WriteLine("執(zhí)行完成For.");
  10.         }
  11.         private void Demo8()
  12.         {
  13.             List<int> data = new List<int>();
  14.             Parallel.ForEach(Program.Data, (i) =>
  15.             {
  16.                 if (Program.Data[i] % 2 == 0)
  17.                     data.Add(Program.Data[i]);
  18.             });
  19.             Console.WriteLine("執(zhí)行完成ForEach.");
  20.         }

看起來(lái)應(yīng)該是沒有問(wèn)題的,但是我們多次運(yùn)行后會(huì)發(fā)現(xiàn),偶爾會(huì)出現(xiàn)錯(cuò)誤如下:

image

這是因?yàn)長(zhǎng)ist是非線程安全的類,我們需要使用System.Collections.Concurrent命名空間下的類型來(lái)用于并行循環(huán)體內(nèi)。

說(shuō)明
BlockingCollection<T> 為實(shí)現(xiàn) IProducerConsumerCollection<T> 的線程安全集合提供阻止和限制功能。
ConcurrentBag<T> 表示對(duì)象的線程安全的無(wú)序集合。
ConcurrentDictionary<TKey, TValue> 表示可由多個(gè)線程同時(shí)訪問(wèn)的鍵值對(duì)的線程安全集合。
ConcurrentQueue<T> 表示線程安全的先進(jìn)先出 (FIFO) 集合。
ConcurrentStack<T> 表示線程安全的后進(jìn)先出 (LIFO) 集合。
OrderablePartitioner<TSource> 表示將一個(gè)可排序數(shù)據(jù)源拆分成多個(gè)分區(qū)的特定方式。
Partitioner 提供針對(duì)數(shù)組、列表和可枚舉項(xiàng)的常見分區(qū)策略。
Partitioner<TSource> 表示將一個(gè)數(shù)據(jù)源拆分成多個(gè)分區(qū)的特定方式。

公共類

那么我們上面的代碼可以修改為,加了了ConcurrentQueue和ConcurrentStack的最基本的操作。

Code
  1.         /// <summary>
  2.         /// 并行循環(huán)操作集合類,集合內(nèi)只取5個(gè)對(duì)象
  3.         /// </summary>
  4.         private void Demo7()
  5.         {
  6.             ConcurrentQueue<int> data = new ConcurrentQueue<int>();
  7.             Parallel.For(0, Program.Data.Count, (i) =>
  8.             {
  9.                 if (Program.Data[i] % 2 == 0)
  10.                     data.Enqueue(Program.Data[i]);//將對(duì)象加入到隊(duì)列末尾
  11.             });
  12.             int R;
  13.             while (data.TryDequeue(out R))//返回隊(duì)列中開始處的對(duì)象
  14.             {
  15.                 Console.WriteLine(R);
  16.             }
  17.             Console.WriteLine("執(zhí)行完成For.");
  18.         }
  19.         /// <summary>
  20.         /// 并行循環(huán)操作集合類
  21.         /// </summary>
  22.         private void Demo8()
  23.         {
  24.             ConcurrentStack<int> data = new ConcurrentStack<int>();
  25.             Parallel.ForEach(Program.Data, (i) =>
  26.             {
  27.                 if (Program.Data[i] % 2 == 0)
  28.                     data.Push(Program.Data[i]);//將對(duì)象壓入棧中
  29.             });
  30.             int R;
  31.             while (data.TryPop(out R))//彈出棧頂對(duì)象
  32.             {
  33.                 Console.WriteLine(R);
  34.             }
  35.             Console.WriteLine("執(zhí)行完成ForEach.");
  36.         }

ok,這里返回一個(gè)序列的問(wèn)題也解決了。

結(jié)論3:在并行循環(huán)內(nèi)重復(fù)操作的對(duì)象,必須要是thread-safe(線程安全)的。集合類的線程安全對(duì)象全部在System.Collections.Concurrent命名空間下。

  

  

四、返回集合運(yùn)算結(jié)果/含有局部變量的并行循環(huán)

        使用循環(huán)的時(shí)候經(jīng)常也會(huì)用到迭代,那么在并行循環(huán)中叫做 含有局部變量的循環(huán) 。下面的代碼中詳細(xì)的解釋,這里就不啰嗦了。

Code
  1.         /// <summary>
  2.         /// 具有線程局部變量的For循環(huán)
  3.         /// </summary>
  4.         private void Demo9()
  5.         {
  6.             List<int> data = Program.Data;
  7.             long total = 0;
  8.             //這里定義返回值為long類型方便下面各個(gè)參數(shù)的解釋
  9.             Parallel.For<long>(0,           // For循環(huán)的起點(diǎn)
  10.                 data.Count,                 // For循環(huán)的終點(diǎn)
  11.                 () => 0,                    // 初始化局部變量的方法(long),既為下面的subtotal的初值
  12.                 (i, LoopState, subtotal) => // 為每個(gè)迭代調(diào)用一次的委托,i是當(dāng)前索引,LoopState是循環(huán)狀態(tài),subtotal為局部變量名
  13.                 {
  14.                     subtotal += data[i];    // 修改局部變量
  15.                     return subtotal;        // 傳遞參數(shù)給下一個(gè)迭代
  16.                 },
  17.                 (finalResult) => Interlocked.Add(ref total, finalResult) //對(duì)每個(gè)線程結(jié)果執(zhí)行的最后操作,這里是將所有的結(jié)果相加
  18.                 );
  19.             Console.WriteLine(total);
  20.         }
  21.         /// <summary>
  22.         /// 具有線程局部變量的ForEach循環(huán)
  23.         /// </summary>
  24.         private void Demo10()
  25.         {
  26.             List<int> data = Program.Data;
  27.             long total = 0;
  28.             Parallel.ForEach<int, long>(data, // 要循環(huán)的集合對(duì)象
  29.                 () => 0,                      // 初始化局部變量的方法(long),既為下面的subtotal的初值
  30.                 (i, LoopState, subtotal) =>   // 為每個(gè)迭代調(diào)用一次的委托,i是當(dāng)前元素,LoopState是循環(huán)狀態(tài),subtotal為局部變量名
  31.                 {
  32.                     subtotal += i;            // 修改局部變量
  33.                     return subtotal;          // 傳遞參數(shù)給下一個(gè)迭代
  34.                 },
  35.                 (finalResult) => Interlocked.Add(ref total, finalResult) //對(duì)每個(gè)線程結(jié)果執(zhí)行的最后操作,這里是將所有的結(jié)果相加
  36.                 );
  37.             Console.WriteLine(total);
  38.         }

結(jié)論4:并行循環(huán)中的迭代,確實(shí)很傷人。代碼太難理解了。

 

 

五、PLinq(Linq的并行計(jì)算)

           上面介紹完了For和ForEach的并行計(jì)算盛宴,微軟也沒忘記在Linq中加入并行計(jì)算。下面介紹Linq中的并行計(jì)算。

4.0中在System.Linq命名空間下加入了下面幾個(gè)新的類:

說(shuō)明
ParallelEnumerable 提供一組用于查詢實(shí)現(xiàn) ParallelQuery{TSource} 的對(duì)象的方法。這是 Enumerable 的并行等效項(xiàng)。
ParallelQuery 表示并行序列。
ParallelQuery<TSource> 表示并行序列。

原理2:PLinq最多會(huì)開啟64個(gè)線程

原理3:PLinq會(huì)自己判斷是否可以進(jìn)行并行計(jì)算,如果不行則會(huì)以順序模式運(yùn)行。

原理4:PLinq會(huì)在昂貴的并行算法或成本較低的順序算法之間進(jìn)行選擇,默認(rèn)情況下它選擇順序算法。

  

在ParallelEnumerable中提供的并行化的方法

ParallelEnumerable 運(yùn)算符 說(shuō)明
AsParallel() PLINQ 的入口點(diǎn)。指定如果可能,應(yīng)并行化查詢的其余部分。
AsSequential() 指定查詢的其余部分應(yīng)像非并行 LINQ 查詢一樣按順序運(yùn)行。
AsOrdered() 指定 PLINQ 應(yīng)保留查詢的其余部分的源序列排序,直到例如通過(guò)使用 orderby 子句更改排序?yàn)橹埂?/td>
AsUnordered() 指定查詢的其余部分的 PLINQ 不需要保留源序列的排序。
WithCancellation() 指定 PLINQ 應(yīng)定期監(jiān)視請(qǐng)求取消時(shí)提供的取消標(biāo)記和取消執(zhí)行的狀態(tài)。
WithDegreeOfParallelism() 指定 PLINQ 應(yīng)當(dāng)用來(lái)并行化查詢的處理器的最大數(shù)目。
WithMergeOptions() 提供有關(guān) PLINQ 應(yīng)當(dāng)如何(如果可能)將并行結(jié)果合并回到使用線程上的一個(gè)序列的提示。
WithExecutionMode() 指定 PLINQ 應(yīng)當(dāng)如何并行化查詢(即使默認(rèn)行為是按順序運(yùn)行查詢)。
ForAll() 多線程枚舉方法,與循環(huán)訪問(wèn)查詢結(jié)果不同,它允許在不首先合并回到使用者線程的情況下并行處理結(jié)果。
Aggregate() 重載 對(duì)于 PLINQ 唯一的重載,它啟用對(duì)線程本地分區(qū)的中間聚合以及一個(gè)用于合并所有分區(qū)結(jié)果的最終聚合函數(shù)。

下面是PLinq的簡(jiǎn)單代碼

Code
  1.         /// <summary>
  2.         /// PLinq簡(jiǎn)介
  3.         /// </summary>
  4.         private void Demo11()
  5.         {
  6.             var source = Enumerable.Range(1, 10000);
  7.             //查詢結(jié)果按source中的順序排序
  8.             var evenNums = from num in source.AsParallel().AsOrdered()
  9.                        where num % 2 == 0
  10.                        select num;
  11.             //ForAll的使用
  12.             ConcurrentBag<int> concurrentBag = new ConcurrentBag<int>();
  13.             var query = from num in source.AsParallel()
  14.                         where num % 10 == 0
  15.                         select num;
  16.             query.ForAll((e) => concurrentBag.Add(e * e));
  17.         }

上面代碼中使用了ForAll,F(xiàn)orAll和foreach的區(qū)別如下:

image

PLinq的東西很繁雜,但是都只是幾個(gè)簡(jiǎn)單的方法,熟悉下方法就好了。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    国产中文字幕一区二区| 久久99这里只精品热在线| 国产又爽又猛又粗又色对黄| 国产又大又猛又粗又长又爽| 国产视频福利一区二区| 日本黄色美女日本黄色| 亚洲国产av在线观看一区| 又大又紧又硬又湿又爽又猛| 91爽人人爽人人插人人爽| 国产女性精品一区二区三区| 精品人妻一区二区三区四在线| 中文字幕乱子论一区二区三区 | 熟女一区二区三区国产| 精品一区二区三区乱码中文| 91欧美亚洲精品在线观看| 又色又爽又无遮挡的视频| 97人妻人人揉人人躁人人| 亚洲综合日韩精品欧美综合区| 麻豆国产精品一区二区| 日韩毛片视频免费观看| 国产亚洲系列91精品| 青草草在线视频免费视频| 欧美性高清一区二区三区视频| 日韩一级欧美一级久久| 亚洲精品美女三级完整版视频| 中文字幕一区二区熟女| 夫妻激情视频一区二区三区| 亚洲中文在线中文字幕91| 精品女同在线一区二区| 殴美女美女大码性淫生活在线播放| 国产一区欧美午夜福利| 中日韩美一级特黄大片| 欧美日韩免费观看视频| 国产一区欧美一区日韩一区| 欧美欧美日韩综合一区| 深夜视频在线观看免费你懂| 97人妻人人揉人人躁人人| 国产精品白丝一区二区| 一个人的久久精彩视频| 午夜福利直播在线视频| 九九热精彩视频在线免费 |