天天看點

微軟并行程式設計類庫Parallel Extensions初探 Part1

概述

Microsoft Parallel Extensions to the .NET Framework 3.5是一個托管程式設計模型,用于資料并行化和任務并行化,并可對統一在共同的工作排程程式之下的并行硬體進行協調。 Parallel Extensions to the .NET Framework 3.5使開發者更容易編寫出充分發揮并行硬體的優勢的程式,不但能随着處理器數量的增長而提高性能,而且避免了許多舊有并發程式設計模型的複雜性。

你可以從這裡​​下載下傳Microsoft Parallel Extensions to the .NET Framework 3.5​​ June 2008 CTP版本,安裝後會注冊一個System.Threading.dll程式集到GAC中。Parallel Extensions主要由兩部分組成:Task Parallel Library(TPL)和Parallel LINQ (PLINQ),它們将會內建在.NET Framework 4.0中。

簡單調用

在開始之前,我們不妨停下來思考幾個問題:如果有多個線程在同一時間通路同一個變量,它們之間可能會互相影響,該如何解決?如果有多個線程同時鎖住了一些資源,由于互相等待而造成死鎖,該如何解決?如果覺的這些問題很難解決,那就不要思考了,有了Parallel.Invoke,無需再去考慮這些令人頭疼的問題,我們先定義三個任務:

private void Task1()
{
    Thread.Sleep(1000);
}
private void Task2()
{
    Thread.Sleep(2000);
}
private void Task3()
{
    Thread.Sleep(3000);
}      

并行調用這三個任務,隻需要一句話:

Parallel.Invoke(Task1, Task2, Task3);      

除此之外,還可以把所有的任務放在一個Action資料組中,再進行調用,如下代碼片段,這在某些場景中會非常的有用,我們在設計階段無需考慮最終運作時将會有多少個任務會執行:

Action[] actions = { Task1, Task2, Task3 };
Parallel.Invoke(actions);      

我們不妨對Parallel.Invoke做一個簡單的測試,如下代碼片段所示:

private long InvokeSequential()
{
    Stopwatch watch = new Stopwatch();
    watch.Start();
    Task1();
    Task2();
    Task3();
    watch.Stop();

    return watch.ElapsedMilliseconds;
}

private long InvokeParallel()
{
    Stopwatch watch = new Stopwatch();
    watch.Start();
    Parallel.Invoke(Task1, Task2, Task3);
    watch.Stop();

    return watch.ElapsedMilliseconds;
}

private long InvokeParallelArray()
{
    Stopwatch watch = new Stopwatch();
    watch.Start();
    Action[] actions = { Task1, Task2, Task3 };
    Parallel.Invoke(actions);
    watch.Stop();

    return watch.ElapsedMilliseconds;
}      

來看看最後的結果:

微軟并行程式設計類庫Parallel Extensions初探 Part1

循環調用

循環執行某件事情應該是我們程式設計中經常遇到的問題,但是之前所有的循環隻能順序的進行執行,如下面這段代碼,再平常不過了:

for (int i = 0; i < 10; i++)
{
    Compute(i);
}       

在Parallel Extensions中,可以使用Parallel.For來并行的執行循環任務:

Parallel.For(0, 10,
        delegate (int i){
            Compute(i);
        }
    );      

甚至有了Lambda表達式,還可以更簡單的編寫為:

Parallel.For(0, 10,
        i => { Compute(i);}
    );      

現在,我們再來做一個簡單的測試,代碼如下:

private static long SequentialForLoop()
{
    Stopwatch watch = new Stopwatch();
    watch.Start();
    for (int i = 0; i < 10; i++)
    {
        Compute(i);
    } 
    watch.Stop();
    return watch.ElapsedMilliseconds;
}

private static long ParallelForLoop()
{
    Stopwatch watch = new Stopwatch();
    watch.Start();
    Parallel.For(0, 10,
            i => { Compute(i);}
        );
    watch.Stop();
    return watch.ElapsedMilliseconds;
}

private static void Compute(int i)
{
    Thread.Sleep(200 * i);
}      

測試結果如下:

微軟并行程式設計類庫Parallel Extensions初探 Part1

類似的在Parallel中還提供了Parallel.ForEach方法,如下圖所示:

微軟并行程式設計類庫Parallel Extensions初探 Part1
List<int> data = new List<int> { 1, 2, 3, 4, 5 };
Parallel.ForEach(
        data,
        i => { Compute(i); }
    );      

總結