線程
C#線程的生命週期分爲幾個階段:
1、未啓動狀態:當線程實例被創建但 Start 方法未被調用時的狀況;
2、就緒狀態:當線程準備好運行並等待 CPU 週期時的狀況;
3、不可運行狀態:下面的幾種情況下線程是不可運行的:
1). 已經調用 Sleep 方法;
2). 已經調用 Wait 方法;
3). 通過 I/O 操作阻塞。
4、死亡狀態:當線程已完成執行或已中止時的狀況。
一個簡單的不帶參數的線程:
//線程函數
public static void Task()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Task Print {0}", i);
Thread.Sleep(1000);
}
}
//創建簡單的線程,不帶任何參數
Thread th = new Thread(Task);
th.Name = "PrintThread";
//啓動執行線程
th.Start();
//等待線程執行完畢
th.Join();
Console.WriteLine("Thread [{0}] Finished !", th.Name);
對於帶參數的線程方法,通常需要自定義一個線程參數類,示例:
//自定義的線程參數類
class ThreadParameter
{
private int size;
public ThreadParameter(int s)
{
size = s;
}
//線程方法
public void ThreadMethod()
{
for (int i = 0; i < size; i++)
{
Console.WriteLine("Task1 Print {0}", i);
Thread.Sleep(1000);
}
}
}
//實例化線程參數類
ThreadParameter tp = new ThreadParameter(10);
//實例化線程對象
Thread th = new Thread(tp.ThreadMethod);
th.Name = "PrintThread";
//啓動線程
th.Start();
//等待線程結束
th.Join();
Console.WriteLine("Thread [{0}] Finished !", th.Name);
Abort() 方法用於銷燬線程。通過拋出 threadabortexception 在運行時中止線程。這個異常不能被捕獲,如果有 finally 塊,控制會被送至 finally 塊。
public static void ThreadProc()
{
try
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Task1 Print {0}", i);
Thread.Sleep(1000);
}
}
//捕獲線程異常退出
catch (ThreadAbortException e)
{
Console.WriteLine(e);
}
finally
{
Console.WriteLine("Thread Finished !");
}
}
//實例化線程對象
Thread th = new Thread(ThreadProc);
//啓動線程
th.Start();
//等待5秒後結束線程
Thread.Sleep(5000);
th.Abort();
Console.WriteLine("Thread [{0}] Finished !", th.Name);
異步
有時候一個函數的執行很長,不能一直等待函數執行完畢,希望在函數執行的時候主線程可以完成些其他的事情,然後再等待線程執行完畢。下面的代碼同步執行時會依次打印1~5數字,而異步執行的時候則是亂序的:
static void Test(string name)
{
Console.WriteLine("TestMethod: {0:yyyy-MM-dd HH:mm:ss.fff} {1}", DateTime.Now, name);
}
static void Main()
{
TestDelegate d = Test;
Console.WriteLine("Beginning : {0:yyyy-MM-dd HH:mm:ss.fff}", DateTime.Now);
//同步執行,依次打印
d("111");
d("222");
d("333");
d("444");
d("555");
//異步執行,會放入線程池中,打印亂序
var r1 = d.BeginInvoke("小明1", null, null);
var r2 = d.BeginInvoke("小明2", null, null);
var r3 = d.BeginInvoke("小明3", null, null);
var r4 = d.BeginInvoke("小明4", null, null);
var r5 = d.BeginInvoke("小明5", null, null);
//此時異步執行還沒有完成
Console.WriteLine("End : {0:yyyy-MM-dd HH:mm:ss.fff}", DateTime.Now);
//等待異步執行完成
d.EndInvoke(r1);
d.EndInvoke(r2);
d.EndInvoke(r3);
d.EndInvoke(r4);
d.EndInvoke(r5);
Console.WriteLine("線程全部執行完成");
Console.Read();
}
另外一個定時器定時打印的例子:
//打印當前日期和時間
static void PrintPoint(object sender, ElapsedEventArgs e)
{
DateTime dtCurr = DateTime.Now;
Console.WriteLine("{0:yyyy-MM-dd HH:mm:ss.fff}", dtCurr);
Thread.Sleep(1000);
}
static void Main()
{
//構建定時器,
System.Timers.Timer timer = new System.Timers.Timer();
timer.Interval = 1000;
timer.Start();
//連接打印方式到定時器超時事件
timer.Elapsed += new ElapsedEventHandler(PrintPoint);
Console.WriteLine("End");
Console.Read();
}
在主線程中使用EndInvoke還是很麻煩,始終要阻塞等待。使用異步回調就可以解決這個問題,當異步返回時自動調用回調函數:
//異步結束時的回調
public static void BackCall(IAsyncResult parameter)
{
//parameter.IsCompleted用於判斷異步方法是否已調用完成;
if (parameter.IsCompleted)
{
//通過EndInvoke方法獲取異步方法的返回結果(類型與異步方法的結果一致)
Console.Write(string.Format("回調完成,返回值"));
}
else
{
Console.Write("調用未完成");
}
}
TestDelegate td = PrintPoint;
td.BeginInvoke(BackCall, null);
Console.WriteLine("End");
Console.Read();