[.net 面向對象程序設計進階] (18) 多線程(Multithreading)(三) 利用多線程提升程序性能(下)

[.net 面向對象程序設計進階] (18) 多線程(Multithreading)(二) 利用多線程提升程序性能(下)html

本節導讀:編程

上節說了線程同步中使用線程鎖和線程通知的方式來處理資源共享問題,這些是多線程的基本原理。多線程

.NET 4.0之後對多線程的實現變得更簡單了。異步

本節主要討論.NET4.0多線程的新特性——使用Task類建立多線程。性能

讀前必備:學習

A. LINQ使用  [.net 面向對象編程基礎] (20) LINQ使用spa

B. 泛型          [.net 面向對象編程基礎] (18) 泛型.net

1. 線程池ThreadPool pwa

在介紹4.0之後的多線程新特徵以前,先簡單說一下線程池。線程

經過前面對多線程的學習,咱們發現多線程的建立和使用並不難,難的在於多線程的管理,特別是線程數量級不少的狀況下,如何進行管理和資源釋放。須要使用線程池來解決。

簡單來講線程池就是.NET提供的存放線程的一個對象容器。

線程池線程分爲兩類:工做線程和IO線程.
線程池是一種多線程處理形式,處理過程當中將任務添加到隊列,而後在建立線程後自動啓動這些任務。

對於線程池,可以使用要運行的過程的委託調用 System.Threading.ThreadPool.QueueUserWorkItem(System.Threading.WaitCallback) 方法

下面是一個線程池的示例:

先設置一個建立線程總數靜態字段:

 static readonly int totalThreads = 20;

使用線程池建立線程:

//設置最小線程和最大線程數
ThreadPool.SetMinThreads(2, 2);
ThreadPool.SetMaxThreads(20, 20);

for (int i = 0; i < totalThreads; i++)
{
    ThreadPool.QueueUserWorkItem(o =>
    {
        Thread.Sleep(1000);
        int a, b;
        ThreadPool.GetAvailableThreads(out a, out b);
        Console.WriteLine(string.Format("({0}/{1}) #{2} : {3}", a, b, Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString()));
    });
}
Console.WriteLine("主線程完成");

運行結果以下:

 

2. Task

ThreadPoolQueueUserWorkItem()方法發起一次異步的線程執行很簡單,可是該方法最大的問題是沒有一個內建的機制讓你知道操做何時完成,有沒有一個內建的機制在操做完成後得到一個返回值。爲此,在.NET 4.0 之後,咱們可使用System.Threading.Tasks中的Task類。這也是.NET 4.0之後多線程的推薦作法。

構造一個Task<T>對象,併爲泛型T參數傳遞一個操做的返回類型。

Task類可使用多種方法建立多線程,下面詳細介紹。 

2.1 使用Factory屬性 

Task 實例能夠用各類不一樣的方式建立。 最多見的方法是使用任務的 Factory 屬性檢索可用來建立用於多個用途的TaskFactory 實例。

 例如,要建立運行操做的 Task,可使用工廠的 StartNew 方法:          

//最簡單的線程示例
Task.Factory.StartNew(() =>
{
    Console.WriteLine("我是使用Factory屬性建立的線程");
});

運行結果以下:

若是想簡單的建立一個Task,那麼使用Factory.NewStart()來建立,很簡便。

若是像對所建立的Task附加更多的定製和設置特定的屬性,請繼續往下看。

2.2 使用Task實例實現多線程 

//簡單的Task實例建立線程
Action<object> action = (object obj) =>
{
    Console.WriteLine("Task={0}, obj={1}, Thread={2}", Task.CurrentId, obj.ToString(), Thread.CurrentThread.ManagedThreadId);
};
Task t1 = new Task(action, "參數");
t1.Start();

 運行結果以下:

 

//簡寫上面實例,並建立100個線程
System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
int m = 100;
Task[] tasks = new Task[m];
for (int i = 0; i < m; i++)
{
    tasks[i] = new Task((object obj) =>
        {
            Thread.Sleep(200);
            Console.WriteLine("Task={0}, obj={1}, Thread={2},當前時間:{3}",
            Task.CurrentId, obj.ToString(),
            Thread.CurrentThread.ManagedThreadId,
            System.DateTime.Now.ToString());
        }, "參數" + i.ToString()
    );
    tasks[i].Start();
}
           
Task.WaitAll(tasks);
Console.WriteLine("線程耗時:{0},當前時間:{1}" ,watch.ElapsedMilliseconds,System.DateTime.Now.ToString());

運行結果以下:

 2.3 Task傳入參數

上面介紹了使用一個Action委託來完成任程,那麼給線程中傳入參數,就可使用System.Action<object>來完成。

 傳入一個參數的示例:

/// <summary>
/// 一個參數的方法
/// </summary>
/// <param name="parameter"></param>
static void MyMethod(string parameter)
{
    Console.WriteLine("{0}", parameter);
}

調用以下:

//Task傳入一個參數
Task myTask = new Task((parameter) => MyMethod(parameter.ToString()), "aaa");
myTask.Start();

運行結果以下:

 傳入多個參數以下:

/// <summary>
/// 多個參數的方法
/// </summary>
/// <param name="parameter1"></param>
/// <param name="parameter2"></param>
/// <param name="parameter3"></param>
static void MyMethod(string parameter1,int parameter2,DateTime parameter3)
{
    Console.WriteLine("{0} {1} {2}", parameter1,parameter2.ToString(),parameter3.ToString());
}

調用以下:

//Task傳入多個參數
for (int i = 1; i <= 20; i++)
{              
    new Task(() => { MyMethod("個人線程", i, DateTime.Now); }).Start();
    Thread.Sleep(200);
}

運行結果以下:

 

 對於傳入多個參數,可使用無參數委託包裝一個多參數的方法來完成。 

2.4 Task的結果

要獲取Task的結果,在建立Task的時候,就要採用Task<T>來實例化一個Task。

其中的T就是Task執行完成以後返回結果的類型。

經過Task實例的Result屬性就能夠獲取結果。

System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
Task<int> myTask = new Task<int>(() =>
{
    int sum = 0;
    for (int i = 0; i < 10000; i++)
        sum += i;
    return sum;
});
myTask.Start();           
Console.WriteLine("結果: {0} 耗時:{1}", myTask.Result,watch.ElapsedMilliseconds);

運行結果以下:

使用Factory屬性來完成上面的示例:

//使用Factory屬性建立
System.Diagnostics.Stopwatch watchSecond = System.Diagnostics.Stopwatch.StartNew();
Task<int> myTaskSecond = Task.Factory.StartNew<int>(() =>
{
    int sum = 0;
    for (int i = 0; i < 10000; i++)
        sum += i;
    return sum;
});            
Console.WriteLine("結果: {0} 耗時:{1}", myTaskSecond.Result, watchSecond.ElapsedMilliseconds);

運行結果以下:

多線程除以上的一些基礎知識,在處理各類並行任務和多核編程中的使用,小夥伴能夠參考專門關於多線程的書籍學習。

想要徹底深刻的學習多線程須要慢慢修煉,不斷積累。 

3. 本節要點:

A.本點簡單介紹了線程池ThreadPool的使用;

B.介紹一使用Task進行多線程建立及Tast的參數傳入和返回結果。

==============================================================================================

返回目錄

<若是對你有幫助,記得點一下推薦哦,若有有不明白或錯誤之處,請多交流>

<對本系列文章閱讀有困難的朋友,請先看《.net 面向對象編程基礎》>

<轉載聲明:技術須要共享精神,歡迎轉載本博客中的文章,但請註明版權及URL>

.NET 技術交流羣:467189533 .NET 程序設計

==============================================================================================

相關文章
相關標籤/搜索