多線程程式設計基礎案例
一、使用線程的理由
1、可以使用線程将代碼同其他代碼隔離,提高應用程式的可靠性。
2、可以使用線程來簡化編碼。
3、可以使用線程來實作并發執行。
二、基本知識
1、程序與線程:程序作為作業系統執行程式的基本機關,擁有應用程式的資源,程序包含線程,程序的資源被線程共享,線程不擁有資源。
2、前台線程和背景線程:通過Thread類建立線程預設為前台線程。當所有前台線程關閉時,所有的背景線程也會被直接終止,不會抛出異常。
3、挂起(Suspend)和喚醒(Resume):由于線程的執行順序和程式的執行情況不可預知,是以使用挂起和喚醒容易發生死鎖的情況,在實際應用中應該盡量少用。
4、阻塞線程:Join,阻塞調用線程,直到該線程終止。
5、終止線程:Abort:抛出 ThreadAbortException 異常讓線程終止,終止後的線程不可喚醒。Interrupt:抛出 ThreadInterruptException 異常讓線程終止,通過捕獲異常可以繼續執行。
6、線程優先級:Highest AboveNormal Normal BelowNormal Lowest ,預設為Normal。
三、線程的使用
線程函數通過委托傳遞,可以不帶參數,也可以帶參數(隻能有一個參數),可以用一個類或結構體封裝參數。
案例:
class Program
{
static void Main(string[] args)
{
Thread t1 = new Thread(new ThreadStart(TestMethod));//建立無參數數線程
Thread t2 = new Thread(new ParameterizedThreadStart(TestMethod));//建立帶參數的線程
//設定為背景程序
t1.IsBackground = true;
t2.IsBackground = true;
t1.Start();
t2.Start("hello");
Console.ReadKey();
}
public static void TestMethod()
{
Console.WriteLine("不帶參數的線程函數");
}
//參數要定義為object 類型
public static void TestMethod(object data)
{
string datastr = data as string;
Console.WriteLine("帶參數的線程函數,參數為:{0}", datastr);
}
}
四、線程池
由于線程的建立和銷毀需要耗費一定的開銷,過多的使用線程會造成記憶體資源的浪費,出于對性能的考慮,于是引入了線程池的概念。線程池維護一個請求隊列,線程池的代碼從隊列提取任務,然後委派給線程池的一個線程執行,線程執行完不會被立即銷毀,這樣既可以在背景執行任務,又可以減少線程建立和銷毀所帶來的開銷。
線程池線程預設為背景線程(IsBackground)。
class Program
{
static void Main(string[] args)
{
//将工作項加入到線程池隊列中,這裡可以傳遞一個線程參數
ThreadPool.QueueUserWorkItem(TestMethod, "Hello");
Console.ReadKey();
}
//線程函數
public static void TestMethod(object data)
{
string datastr = data as string;
Console.WriteLine(datastr);
}
}
五、Task類
使用ThreadPool的QueueUserWorkItem()方法發起一次異步的線程執行很簡單,但是該方法最大的問題是沒有一個内建的機制讓你知道操作什麼時候完成,有沒有一個内建的機制在操作完成後獲得一個傳回值。為此,可以使用System.Threading.Tasks中的Task類。
構造一個Task<TResult>對象,并為泛型TResult參數傳遞一個操作的傳回類型。
class Program
{
static void Main(string[] args)
{
Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 1000);
t.Start();
t.Wait();
Console.WriteLine(t.Result);
Console.ReadKey();
}
private static Int32 Sum(Int32 n)
{
Int32 sum = 0;
for (; n > 0; --n)
checked{ sum += n;} //結果太大,抛出異常
return sum;
}
}
一個任務完成時,自動啟動一個新任務。
一個任務完成後,它可以啟動另一個任務,下面重寫了前面的代碼,不阻塞任何線程。
class Program
{
static void Main(string[] args)
{
Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 1000);
t.Start();
//t.Wait();
Task cwt = t.ContinueWith(task => Console.WriteLine("The result is {0}", t.Result));
Console.ReadKey();
}
private static Int32 Sum(Int32 n)
{
Int32 sum = 0;
for (; n > 0; --n)
checked { sum += n; } //結果溢出,抛出異常
return sum;
}
}
六、委托異步執行
委托的異步調用:BeginInvoke() 和 EndInvoke()
namespace Test
{
public delegate string MyDelegate(object data);
class Program
{
static void Main(string[] args)
{
MyDelegate mydelegate = new MyDelegate(TestMethod);
IAsyncResult result = mydelegate.BeginInvoke("Thread Param", TestCallback, "Callback Param");
//異步執行完成
string resultstr = mydelegate.EndInvoke(result);
}
//線程函數
public static string TestMethod(object data)
{
string datastr = data as string;
Console.WriteLine(datastr);
return datastr;
}
//異步回調函數
public static void TestCallback(IAsyncResult data)
{
Console.WriteLine(data.AsyncState);
}
}
}
異步回調函數在上面線程函數執行結束後,将要退出時執行。
本文來自部落格園,作者:農碼一生,轉載請注明原文連結:https://www.cnblogs.com/wml-it/p/14808683.html
技術的發展日新月異,随着時間推移,無法保證本部落格所有内容的正确性。如有誤導,請大家見諒,歡迎評論區指正! 個人開源代碼連結: GitHub:https://github.com/ITMingliang
Gitee:https://gitee.com/mingliang_it
GitLab:https://gitlab.com/ITMingliang
進開發學習交流群: