最近花了近兩周時間讀完了C#本質論,這本書非常喜歡,但是到後面關於多線程和同步這塊,讀起來就感覺有些困難了,所以做了筆記,一方面防止忘記,另一方法如果有不正確的地方,十分歡喜各位前輩不吝賜教 什麼是單線程 通過一個控制台程式來認識單線程 在Console.WriteLine處添加一個斷點,查看主線程 ...
最近花了近兩周時間讀完了C#本質論,這本書非常喜歡,但是到後面關於多線程和同步這塊,讀起來就感覺有些困難了,所以做了筆記,一方面防止忘記,另一方法如果有不正確的地方,十分歡喜各位前輩不吝賜教
什麼是單線程
通過一個控制台程式來認識單線程
static void Main(string[] args)
{
var mainThread = Thread.CurrentThread;
}
在Console.WriteLine處添加一個斷點,查看主線程屬性
ApartmentSate:msdn的大致意思,相同對單元狀態的線程之間可以相互訪問對象,然而在.net中由clr以線程安全的方式管理所有共用資源
CurrentCulture和CurrentUICulture表示區域信息
ExecutionContext:封裝線程相關的上下文信息
IsAlive:如果此線程已啟動並且尚未正常終止或中止,則為 true;否則為 false
IsBackground:表示是否是後臺線程
IsThreadPoolThread:表示是否是線程池線程
ManagedThreadId:托管線程的唯一標識符
更多可查msdn
小結:
關於線程的定義很多地方都有的,我想舉一個例子,很多時候,我們人就是一個線程,早上起床,吃早飯,上班,下班......這一系列事情有序執行就是一個單線程,但是有的時候,一邊聽歌,一邊看小說實際上就開啟了第二個線程了,假如此時再寫代碼,那就是開啟第三個線程了
使用Thread創建一個線程
const int Repetitions = 100;
static void Main(string[] args)
{
ThreadStart threadStart = DoWork;
Thread thread = new Thread(threadStart);
thread.Start();
//Main線程啟動一個迴圈
for (int count = 0; count < Repetitions; count++)
{
Console.Write('-');
}
Console.WriteLine("(主線程最後一個語句...)");
}
static void DoWork()
{
for (int count = 0; count < Repetitions; count++)
{
Console.Write("+");
}
}
Ctrl+F5運行,可以看到,新創建的線程和Main線程中的迴圈是同步執行的(多啟動幾次,會有不一樣的發現哦!)
那麼問題來了,我們創建的線程執行完了嗎?程式到底什麼時候結束?為什麼主線程最後一句話執行完了,創建的線程還在控制台輸出?
修改一下程式,Ctrl+F5,多啟動幾次,會有不一樣的發現哦!
const int Repetitions = 100;
static int index_thread = 0;
static int index_main = 0;
static void Main(string[] args)
{
ThreadStart threadStart = DoWork;
Thread thread = new Thread(threadStart);
thread.Start();
//Main線程啟動一個迴圈
for (int count = 0; count < Repetitions; count++)
{
index_main++;
Console.Write('-');
}
Console.WriteLine($"\nindex_thread:{index_thread}");
Console.WriteLine($"index_main:{index_main}");
Console.WriteLine("(主線程最後一個語句...)");
}
static void DoWork()
{
for (int count = 0; count < Repetitions; count++)
{
index_thread++;
Console.Write("+");
if (count == Repetitions - 1)
{
Debug.Write("我創建的線程執行完成了.....................................\n");
}
}
}
假如你是直接按F5,可以在Visual Studio輸出欄看到
結論:
1.操作系統在所有前臺線程(主線程和新創建的線程都是前臺線程)結束後終止進程,雖然在控制臺中輸出的index_thread不總是100
2.主線程以外的線程執行情況是不確定的,
3.實際上,主線程會等待所有子線程(前臺線程)結束後,結束主線程,關閉進程,結束程式
4.由於子主線程執行情況的不確定性,在主線程輸出index_thread的時候,可能子線程迴圈結束了,也可能沒結束,所以導致結果總是不為100
通過Join方法阻塞主線程,等待子線程執行結束
//省略部分代碼
thread.Join();
Console.WriteLine($"\nindex_thread:{index_thread}");
這樣,就可以保證在此之後,子線程已經運行結束了,每次輸出的結果都為100
使用線程池
const int Repetitions = 1000;
static int index_thread = 0;
static int index_main = 0;
static void Main(string[] args)
{
WaitCallback waitCallBack = DoWork;
ThreadPool.QueueUserWorkItem(waitCallBack, '+');
//Main線程啟動一個迴圈
for (int count = 0; count < Repetitions; count++)
{
index_main++;
Console.Write('-');
}
Console.WriteLine($"\nindex_thread:{index_thread}");
Console.WriteLine($"index_main:{index_main}");
Console.WriteLine("主線程最後一個語句");
}
private static void DoWork(object ch)
{
for (int count = 0; count < Repetitions; count++)
{
index_thread++;
Console.Write(ch);
if (count == Repetitions - 1)
{
Debug.Write("我創建的線程執行完成了.....................................\n");
}
}
}
優點:
1.解決線程太多造成的性能方面的負面影響
2.高效的利用處理器
2.結合lambda使用委托,代碼可以更精簡
註意點
1.使用線程池創建的線程都是後臺線程
2.不要使用線程池運行時間特別長的任務,儘量不要I/O受限
非同步任務
static void Main(string[] args)
{
Task task = Task.Run(()
=>
{
var t = Thread.CurrentThread;
for (int count = 0; count < Repetitions; count++)
{
index_thread++;
Console.Write('+');
}
});
for (int count = 0; count < Repetitions; count++)
{
index_main++;
Console.Write('-');
}
//類似THread.Join方法
task.Wait();
Console.WriteLine($"\nindex_thread:{index_thread}");
Console.WriteLine($"index_main:{index_main}");
Console.WriteLine("Over");
Console.ReadLine();
}
Task是.Net Framwwork4引入的一個類庫,它在使用上比Thread簡單了,可控性又THreadPool強了,預設情況下,它也是從線程池中請求一個線程來執行任務.
與ThreadPool相同的是,當創建時(調用Run)啟動,與Thread相同的是,可以通過Wait()方法阻塞上下文線程(主線程)等待任務執行完成
帶返回值的非同步任務
static void Main(string[] args)
{
Task<string> task = Task.Run(()
=> "string 類型 返回值");
for (int i = 0; i < 1000; i++)
{
if (task.IsCompleted)
{
Console.Write('任務完成了');
break;
}
Console.Write('.');
}
Console.WriteLine(task.Result);
}
泛型的Task表示該任務具有返回值,IsCompleted表示任務是否完成
要註意的是,調用Result屬性的時候,會阻塞上下文進程(內部執行Wait())