一 什么是多線程
1、 什么是進程?一個 exe 運行一次就會產生一個進程,一個 exe 的多個進程之 間數據互相隔離。
2、 一個進程里至少有一個線程:主線程。我們平時寫的控制台程序默認就是單線程的,代 碼從上往下執行,一行執行完了再執行下一行;
3、 什么是多線程:一個人兩件事情同時做效率高。同一時刻一 個人只能干一件事情,其實是在“快速頻繁切換”,如果處理不當可能比不用多線程效率還低
二 Thread 對象
2.1 thread基礎寫法
public static void ThreadTest()
{
int a = 0;
Thread thread1 = new Thread(m=>
{
for (int i = 0; i < 20; i++)
{
a = a + 1;
Console.WriteLine("線程1:"+ a);
}
});
Thread thread2 = new Thread(m =>
{
for (int i = 0; i < 20; i++)
{
a = a + 1;
Console.WriteLine("線程2:"+ a);
}
});
thread1.Start();
thread2.Start();
Console.ReadKey();
}
這段代碼輸出結果如下:
可以看出兩個子線程啟動后是並行執行的,所以輸出結果沒有按照順序來

2.2 設置線程的優先級
thread1.Priority=ThreadPriority。。。
2.3 t1.Join()當前線程等待 t1 線程執行結束,實例如下:
這段代碼執行過后輸出的結果就是正常的從1輸出到了40
public static void ThreadTest() { int a = 0; Thread thread1 = new Thread(m=> { for (int i = 0; i < 20; i++) { a = a + 1; Console.WriteLine("線程1:"+ a); } }); Thread thread2 = new Thread(m => { //等待thread1線程任務完成后在執行 thread1.Join(); for (int i = 0; i < 20; i++) { a = a + 1; Console.WriteLine("線程2:"+ a); } }); //可以將參數傳入到子線程中 thread1.Start(a); //thread1.Join(); 或者將Join放在這里 thread2.Start(a); Console.ReadKey(); }
2.4 Interrupt方法
Interrupt 用於提前喚醒一個在 Sleep 的線程,Sleep 方法會拋出 ThreadInterruptedException 異常 代碼如下:
代碼輸出到9的時候線程會休眠8秒鍾,但是運行到主線程thread1.Interrupt()時,子線程會被喚醒,然后執行catch里面的Console.WriteLine("線程被喚醒");之后接着從10開始輸出到2000。需要注意的是只有線程自身能讓自身休眠
public static void ThreadTest2()
{
Thread thread1 = new Thread(() =>
{
for (int i = 0; i < 2000; i++)
{
if (i==10)
{
//喚醒線程之后會引發ThreadInterruptedException類型的異常,所以需要try catch
try
{
//子線程休眠8秒鍾
Thread.Sleep(8000);
}
catch (ThreadInterruptedException ex)
{
Console.WriteLine("線程被喚醒");
}
}
Console.WriteLine(i);
}
});
thread1.Start();
//提前喚醒在沉睡的子線程
Thread.Sleep(3000);
thread1.Interrupt();
Console.ReadKey();
}
三 線程池
3.1、線程池:因為每次創建線程、銷毀線程都比較消耗 cpu 資源,因此可以通過線程池進行 優化。線程池是一組已經創建好的線程,隨用隨取,用完了不是銷毀線程,然后放到線程池 中,供其他人用。
3.2、用線程池之后就無法對線程進行精細化的控制了(線程啟停、優先級控制等)
3.3、ThreadPool 類的一個重要方法:
static bool QueueUserWorkItem(WaitCallback callBack)
static bool QueueUserWorkItem(WaitCallback callBack, object state)
3.4、除非要對線程進行精細化的控制,否則建議使用線程池,因為又簡單、性能調優又更好。
//QueueUserWorkItem是一個靜態方法不需要New
public static void ThreadPool()
{
System.Threading.ThreadPool.QueueUserWorkItem(m=>
{
for (int i = 0; i < 1000; i++)
{
i++;
Console.WriteLine(i);
}
});
Console.ReadKey();
}
四 TPL風格的異步方法
TPL(Task Parallel Library)是.Net 4.0 之后帶來的新特性,更簡潔,更方便。現在在.Net 平台下已經大面積使用。
注意方法中如果有 await,則方法必須標記為 async,不是所有方法都可以被輕松的標記 為 async。WinForm 中的事件處理方法都可以標記為 async、MVC 中的 Action 方法也可以標 記為 async、控制台的 Main 方法不能標記為 async。 TPL 的特點是:方法都以 XXXAsync 結尾,返回值類型是泛型的 Task<T>。 TPL 讓我們可以用線性的方式去編寫異步程序,不再需要像 EAP 中那樣搞一堆回調、邏 輯跳來跳去了。
/TPL風格返回的Task<T> 泛型的數據
//await 關鍵字等待異步方法返回
public static async void Task()
{
WebClient wc = new WebClient();
string s= await wc.DownloadStringTaskAsync("http://www.baidu.com");
Console.WriteLine(s);
Console.ReadKey();
}
public static void Task2()
{
WebClient wc = new WebClient();
//若果不使用await關鍵字就得使用Task<string>類型來接收數據
Task<string> s2 = wc.DownloadStringTaskAsync("http://www.baidu.com");
Console.ReadKey();
}
自己編寫一個TPL風格的異步方法:
使用了async關鍵字就必須返回Task泛型數據類型的數據
public static Task<string> StringAsync()
{
return Task.Run(() =>
{
Thread.Sleep(5000);
return "hehe";
});
}
// GET: Home
public async Task<ViewResult> Index()
{
var s = await StringAsync();
return View();
}
如果返回值就是一個立即可以隨手可得的值,那么就用 Task.FromResult() 如果是一個需要休息一會的任務(比如下載失敗則過 5 秒鍾后重試。主線程不休息,和 Thread.Sleep 不一樣),那么就用 Task.Delay()。 3、Task.Factory.FromAsync()把 IAsyncResult 轉換為 Task,這樣 APM 風格的 api 也可以用 await 來調 用。 4、編寫異步方法的簡化寫法。如果方法聲明為 async,那么可以直接 return 具體的值,不再用創建 Task,由編譯器創建 Task:
static async Task<int> F1Async()
{
return 1;
}
static Task<int> F2Async()
{
return Task.FromResult(3);
}
static Task<int> F3Async()
{
return Task.Run(()=> {
return 1 + 3; });
}
一定要讓 async 的傳染性(調用異步方法要用 await,用了 await 方法就要聲明為 async,調 用我這個 async 方法的地方必須要 await……)不要輕易直接調用 Task 的 Wait、WaitAll 等方 法。等待一個用 await,而不是 task.Wait();等待多個用 await Task.WhenAll(),而不是 Task.WaitAll()
