Thread 是可以視作程式流程的執行者
執行工作的情境其實沒有很複雜
- 執行工作
- 休息/恢復
- 等待工作執行完成
- 取消執行 (非強制)
- 雖然工作未完成,但強制中斷它,不執行了 (.NET Core 不支援 Thread 等級的中斷)
情境 1-1: 執行工作基本的 thread 建立語法
// 建立工作 | |
ThreadStart doWork = () => | |
{ | |
for (var i = 0; i < 1000; i++) | |
Console.WriteLine($"'{Thread.CurrentThread.Name}' - Do Work - {i}"); | |
}; | |
// 建立兩個 thread(worker)做相同的工作 | |
var worker1 = new Thread(doWork); | |
worker1.Name = "worker 1"; | |
var worker2 = new Thread(doWork); | |
worker2.Name = "worker 2"; | |
worker1.Start(); | |
worker2.Start(); | |
/* | |
'worker 1' - Do Work - 0 | |
'worker 1' - Do Work - 1 | |
'worker 1' - Do Work - 2 | |
'worker 1' - Do Work - 3 | |
'worker 2' - Do Work - 0 | |
'worker 2' - Do Work - 1 | |
.... | |
'worker 1' - Do Work - 97 | |
'worker 2' - Do Work - 99 | |
'worker 1' - Do Work - 98 | |
'worker 1' - Do Work - 99 | |
*/ |
情境 1-2 : 使用 Thread 與 ThreadPool
// 通用的工作 | |
static void DoWork(object? state) | |
{ | |
Console.WriteLine($"{Thread.CurrentThread.Name} - {Thread.CurrentThread.ManagedThreadId} - {Thread.CurrentThread.IsThreadPoolThread} - Hello World."); | |
} | |
// 在主執行緒上執行工作 | |
DoWork(null); | |
// 自己建立 thread 執行工作 | |
var t = new Thread(DoWork); | |
t.Name = "Worker"; | |
t.Start(); | |
// 透過 ThreadPool 執行工作 | |
ThreadPool.QueueUserWorkItem(DoWork); | |
/* | |
Main Thread - 1 - False - work. | |
Worker - 7 - False - work. | |
- 23 - True - work. | |
*/ |
情境 2-1: 休息與恢復
建立一個一開始工作就睡覺的 Worker (常見?)
然後 Manager
做完自己的工作就把Worker叫醒
但是感覺得出來並不是完全自由的控制
例如: Sleep 是由Worker控制,不是由外部控制
想要再次睡著也是很困難
如果 Worker 沒有睡著,但是Manager先去 Interrupt 它也會出錯
// 建立一開始就睡著的工作 | |
// .NET 喚醒的事情很討厭的很多code要寫 | |
ThreadStart doWork = () => | |
{ | |
try | |
{ | |
Console.WriteLine($"'{Thread.CurrentThread.Name}' go to sleep."); | |
Thread.Sleep(Timeout.Infinite); | |
} | |
catch (ThreadInterruptedException) | |
{ | |
Console.WriteLine($"'{Thread.CurrentThread.Name}' awoken."); | |
} | |
catch (ThreadAbortException) | |
{ | |
Console.WriteLine($"'{Thread.CurrentThread.Name}' aborted."); | |
} | |
finally | |
{ | |
Console.WriteLine($"'{Thread.CurrentThread.Name}' executing finally block."); | |
} | |
// 實際的工作在這裡 | |
for (var i = 0; i < 10; i++) | |
Console.WriteLine($"'{Thread.CurrentThread.Name}' - Work - {i}"); | |
}; | |
// 一開始就睡著的 worker | |
var worker = new Thread(doWork); | |
worker.Name = "Worker"; | |
worker.Start(); | |
// Manager 自己的事情做完之後,會叫醒 Worker 來工作 | |
var manager = new Thread(() => | |
{ | |
for (var i = 0; i < 10; i++) | |
Console.WriteLine($"'{Thread.CurrentThread.Name}' - Work - {i}"); | |
worker.Interrupt(); | |
}); | |
manager.Name = "Manager"; | |
manager.Start(); |
情境 2-2: Suspend & Resume (無效)
以前是可以用的 但是現在已經停用了
https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.thread.suspend?view=net-5.0
裡面的理由是因為停止執行緒的時候,會不曉得執行緒裡面的程式碼執行到哪裡
建議使用 Monitor, Mutex, Event, Semaphore等等來達到效果
情境 3: 等待工作執行完成
thread.Join() 有點等待thread抵達的意思
也有可能執行緒在之前就睡著或是消滅了,導致根本不會抵達喔
// 工作一 | |
var worker = new Thread(() => {for(var i = 0 ; i < 10; i++) Console.Write("+");}); | |
worker.Start(); | |
// 工作二 | |
for(var i = 0 ; i < 10; i++) | |
Console.Write("-"); | |
// 等待 worker 完成 | |
worker.Join(); | |
Console.WriteLine("\nEnd\n"); | |
/* 有 worker.Join() 時,不管怎麼樣都會一起 End | |
----------++++++++++ | |
End | |
-++++++++++--------- | |
End | |
*/ | |
/* 沒有 worker.Join() 時,worker 的工作會在 End 之後才做完 | |
---------- | |
End | |
++++++++++ | |
------+----+ | |
End | |
++++++++ | |
*/ |
情境 4: 取消執行(非強制)
.NET 4 之後提供了一個 CancellationToken 來讓我們註記工作的取消
但是仔細想過之後會發現
這些機制其實已經跟
Thread 脫鉤了,只是把取消的機制傳送至工作流程中
不再跟 worker 有關(還記得
Thread 是工作的執行者嗎?)
反而跟 job 有關也就是由流程自己來管理了
個人猜測也是跟整體模型的變更有關係 (Task為主)
所以讓原來控制 Thread 的
Suspend 跟 Resume 也棄用了
var cts = new CancellationTokenSource(); | |
ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), cts.Token); | |
ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), cts.Token); | |
ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), cts.Token); | |
Thread.Sleep(0); | |
Console.WriteLine("大家休息吧!!"); | |
cts.Cancel(); | |
Thread.Sleep(1000); | |
cts.Dispose(); | |
static void DoWork(object obj) { | |
var token = (CancellationToken)obj; | |
for (int i = 0; i < 100000; i++) | |
{ | |
if (token.IsCancellationRequested) | |
{ | |
Console.WriteLine($"worker {Thread.CurrentThread.ManagedThreadId} - 好喔,去休息了!!"); | |
break; | |
} | |
Console.WriteLine($"woker {Thread.CurrentThread.ManagedThreadId} - 工作中"); | |
} | |
} | |
/* | |
...... | |
woker 10 - 工作中 | |
woker 10 - 工作中 | |
大家休息吧!! | |
woker 10 - 工作中 | |
worker 10 - 好喔,去休息了!! | |
woker 22 - 工作中 | |
worker 22 - 好喔,去休息了!! | |
woker 20 - 工作中 | |
worker 20 - 好喔,去休息了!! | |
*/ |
沒有留言:
張貼留言