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


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

本節導讀:

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

.NET 4.0以后對多線程的實現變得更簡單了。

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

讀前必備:

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

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

1. 線程池ThreadPool 

在介紹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 程序設計

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM