重點是總結一些常用用法含義,個人體會他和委託密切相關,尤其是開啓線程,初始化,
第一個知識點:Thread線程開啓,自定義方式解決線程執行完畢回調問題,達成Task.wait()的效果
//進一步如果要調用是帶參數的返回呢?
//定義一個方法,基於傳遞一個委託參數,方法體裏面將這個委託執行的值,作爲參數,傳遞給開啓線程執行另外一個委託,
//緊接着返回這個
private static T CallReturn<T>(Func<T> func)
{
T t = default(T);
//根據參數傳進來的委託,執行完返回的值,進行接收
//開啓新線程,執行新委託
//ThreadStart thread3 = new ThreadStart(()=>{
// t = func.Invoke();
//});
//Thread t3 = new Thread(thread3);
//t3.Start();
//一個多線程,像是異步的感覺
Thread t3 = new Thread(() =>
{
t = func.Invoke();
});
t3.Start();
return t;
}
第二個知識點:線程池,利用額外的ManualResetEvent事件,達成Task.wait()的效果
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ThreadDemo
{
class Program
{
static void Main(string[] args)
{
Func<int> func1 = () =>
{
return DateTime.Now.Year;
};
//Thread開啓線程,但並沒有提供控制線程執行順序的Api,如何做呢?
//如果讓某個線程
Thread thread1 = new Thread(() => {
Thread.Sleep(2000);
Console.WriteLine("我是線程1");
});
//thread1.Start();
//如何讓線程1結束之後,才執行線程2
//**********第一個方式:while()
//while (thread1.ThreadState != ThreadState.Stopped)
//{
// Thread.Sleep(100);
//}
//如果開啓線程沒有停止,阻塞當前主線程,即不執行線程2
//這樣帶來一個問題,當前程序顯示無響應狀態,即卡頓狀態
Thread thread2 = new Thread(() =>
{
//***********第二個方式:Join()
//阻塞線程1,查看官方文檔即可知道它的作用
//直到它執行完成,如果沒有執行完,將一直阻塞,裏面可以傳入一個時間參數
thread1.Join();
Console.WriteLine("我是線程2");
});
//thread2.Start();
//明顯看出,即使線程1先開啓,但是執行時間長,仍然是後執行輸出,
//僅僅是通過代碼先後順序,無法真正執行控制順序
//********第三種方式,兩個委託,放在一個前臺線程中執行
//如下需要兩個委託,順序執行,並且依賴第一個委託的結果,如下獲取年份,
//調用方法,獲取結果應該還是0,爲啥,因爲線程開啓太慢,
//方法中線程還沒來得及開啓
//int res = CallReturn<int>(func1);
Func<int> funres = CallReturnFunc<int>(func1);
//這裏實際上是讓委託在子線程裏執行,
int res = funres.Invoke();
//這裏實際確保讓子線程內容執行完成
}
//進一步如果要調用是帶參數的返回呢?
//定義一個方法,基於傳遞一個委託參數,方法體裏面將這個委託執行的值,作爲參數,傳遞給開啓線程執行另外一個委託,
//緊接着返回這個
private static T CallReturn<T>(Func<T> func)
{
T t = default(T);
//根據參數傳進來的委託,執行完返回的值,進行接收
//開啓新線程,執行新委託
//ThreadStart thread3 = new ThreadStart(()=>{
// t = func.Invoke();
//});
//Thread t3 = new Thread(thread3);
//t3.Start();
//一個多線程,像是異步的感覺
Thread t3 = new Thread(() =>
{
t = func.Invoke();
});
t3.Start();
return t;
}
//線程開啓執行, 還沒來得及執行完,主線程已經執行完畢,值未修改,優化如下:
//返回一個委託,裏面強制要求他執行完,藉助join方法,返回這個委託後,
//調用方法返回值委託的invoke方法即可
//方法後面定義的T,後面才能使用
private static Func<T> CallReturnFunc<T>(Func<T> func1) {
T t=default(T);
Thread thread = new Thread(() => {
t = func1.Invoke();
});
thread.Start();
//確保子線程執行完,返回一個委託,裏面讓join執行,
//同時在外面方法調用返回委託的Invoke方法
//return func1;
return new Func<T>(() => {
thread.Join();
return t;
});
}
//線程功能強大,但是使用起來並不友好,有點難使用,
//問題的關鍵在於線程數量是沒有控制的,在系統資源可用的情況下,可一直使用,
//對於線程的開闢和銷燬都是由程序員來控制,並且這個過程代價比較大
//正是由於這一個特性,提供一個新的方法,即線程池方式,
//線程池的思想,事先創建一部分的線程,放在線程中待用,
//要用直接取來用,用完還回來即可
}
}
這種原生的Thread方式,功能強大,不過掌握起來不太容易,很容易是得不償失,原因是線程創建,關閉都是程序員操作,關鍵是平臺沒有辦法限制,考慮線程創建銷燬,極其佔用資源,就像有一句說的,一個幾歲小孩手持Ak步槍,使用不好,很有可能對自己造成傷害,另外一個思路應運而生,就是線程池,
簡而言之是,不再是創建線程,而是從線程池中申請,取來用,用了還
第二個知識點:線程池,利用額外的ManualResetEvent事件,達成Task.wait()的效果
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ThreadPoolStart
{
class Program
{
//https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.waitcallback?view=netcore-3.1
/*WaitCallback 表示要在 ThreadPool 線程上執行的回調方法。 通過向 WaitCallback 構造函數傳遞迴調方法來創建委託。 你的方法必須具有此處顯示的簽名。
通過將 WaitCallback 委託傳遞給 ThreadPool.QueueUserWorkItem來將方法排隊以便執行。 當線程池線程變得可用時,回調方法會執行。*/
static void Main(string[] args)
{
//如果希望讓線程池中線程等待,通過ManualResetEvent
ManualResetEvent mre = new ManualResetEvent(false);
//一直等待一個信號,有點監聽的意思,等待誰呢?
//等待mre的ret,這個信號執行完成
//開啓一個線程池,簡單方法是直接傳遞一個方法
//默認需要符合WaitCallBack委託的方法,實際上傳值給這個委託類型,無參帶返回值
//第一種方式,直接一個帶參無返回值的方法
//這個方法,有點像是Invoke執行一個委託,
//直接就是執行它
bool b1= ThreadPool.QueueUserWorkItem(TestThreadPool,mre);
//?????????傳一個數據參數,反而不執行
//Thread.Sleep(1);
//new一個WaitCallBack委託
WaitCallback callback = new WaitCallback((o) => {
Console.WriteLine("我是委託實例中調用方法,傳入數據:"+o.ToString());
});
//休眠1秒,仍然只有b2執行,因爲b1還未執行完,隨着主線程結束而結束
bool b2 = ThreadPool.QueueUserWorkItem(callback,"2");
//主線程休眠1ms,等待線程池中線程開始執行其中代碼
//由於線程中線程都是後臺線程,主線程關閉相對應子線程都結束
//Thread.Sleep(1000);
mre.WaitOne();
}
private static void TestThreadPool(object o){
ManualResetEvent mre=(ManualResetEvent)o;
Thread.Sleep(2000);
Console.WriteLine("線程中線程執行回調方法!方法參數:"+o.ToString());
mre.Set();
//這種情況,該如何傳遞這個信號
}
}
}
當然實際多線程使用,使用更加廣泛的是Task,下一篇再講,它使用起來更加方便快捷,不過它的實現原理,也是依賴於線程池,實際上C#語言,實現對線程的控制,主要就是這兩種方式Thread和ThreadPool,其他方式都是進一步封裝