2021年4月18日 星期日

.NET System.Threading - 範例說明 1

Thread 是可以視作程式流程的執行者
執行工作的情境其實沒有很複雜

  1. 執行工作
  2. 休息/恢復
  3. 等待工作執行完成
  4. 取消執行 (非強制)
  5. 雖然工作未完成,但強制中斷它,不執行了 (.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 - 好喔,去休息了!!
*/

沒有留言:

張貼留言