Thread 相關類別
有三個相關的類別:Thread, ThreadStart, ParameterizedThreadStart
ThreadStart
宣告的 function會被 thread 執行,ThreadStart 負責建立無參數的委派函式
ParameterizedThreadStart
宣告的 function會被 thread 執行,ThreadStart 負責建立有參數的委派函式
Thread
建立、控制、管理 Thread,常用的屬性:
屬性 |
說明 |
回傳資料型別 |
CurrentThread |
目前正在執行的 thread |
Thread |
IsAlive |
thread 的執行狀態 |
Boolean |
IsBackground |
thread 是否為背景執行緒 |
Boolean |
ManagedThreadId |
thread 的識別號碼 |
Integer |
Name |
thread 的名稱 |
String 或 null |
ThreadState |
thread 的狀態 |
ThreadState (enum) |
常用 method
method |
說明 |
Abort() |
停止 thread,停止後,無法重新啟動 |
BeginCriticalRegion() |
設定 Critical region |
EndCriticalRegion() |
結束 Critical region |
Interrupt() |
中斷 WaitSleepJoin 狀態的 thread |
Join([a]) |
封鎖執行緒直到停止執行為止,a 為 integer (ms) 或 TimeSpan |
ResetAbort() |
取消正在要求的 Abort() |
Sleep(a) |
暫停執行緒 a (ms) 或 TimeSpan |
Start([a]) |
啟動 thread,a為委派函式的參數,object 型別 |
Note: 使用 Abort() 不保證一定能停止 thread,有可能會發生 exception。ex: SecurityException: 沒有權限停止 thread,ThreadStateException: 停止已暫停的 thread
ThreadState (enum)
位於 System.Threading namespace
列舉常數 |
value |
說明 |
Aborted |
256 |
執行緒目前無作用,但狀態尚未變更為 Stopped |
AbortRequested |
128 |
已收到 Abort() |
Background |
4 |
背景執行 |
Running |
0 |
正在執行 |
Stopped |
16 |
已停止 |
StopRequested |
1 |
已被要求停止中 |
Suspended |
64 |
已暫停 |
SuspendedRequested |
2 |
已被要求暫停中 |
Unstarted |
8 |
還沒開始執行,未被呼叫 Start() |
WaitSleepJoin |
32 |
已被封鎖 |
列出呼叫哪個 method 會導致狀態改變
method |
state |
建立 thread |
Unstarted |
Start() |
Running |
Sleep() |
WaitSleepJoin |
對另一個物件呼叫 Monitor.Wait() |
WaitSleepJoin |
Join |
WaitSleepJoin |
Interrupt() |
Runing |
Suspend() |
SuspendedRequested |
回應 Suspend() |
Suspended |
Resume() |
Running |
Abort() |
AbortRequested |
回應 Abort() |
Stopped |
thread 已終止 |
Stopped |
建立 thread
建立委派的 method
使用 ThreadStart 建立委派物件
使用委派物件建立 Thread 型別的 Thread 物件
Start()
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
bool fgDone;
Int32 sum;
int guess;
public Form1()
{
InitializeComponent();
}
void count()
{
while (sum < int.MaxValue)
sum++;
fgDone = true;
}
void count_param(object num)
{
Random rd = new Random();
while(guess!=(int)num)
{
guess = rd.Next(1, 101);
Thread.Sleep(100);
}
fgDone = true;
}
// 沒有用 thread,計算過程中,視窗會卡住,無法使用
private void Button1_Click(object sender, EventArgs e)
{
textBox1.AppendText("開始計算\r\n");
sum = 0;
while (sum < int.MaxValue)
sum++;
textBox1.AppendText("計算完畢,sum= " +
sum.ToString()+"\r\n");
}
// 用 ThreadStart 產生 Thread
private void button2_Click(object sender, EventArgs e)
{
ThreadStart thdStart = new ThreadStart(count);
Thread thd = new Thread(thdStart);
sum = 0;
fgDone = false;
textBox1.AppendText("執行緒開始執行\r\n");
thd.Start();
timer1.Enabled = true;
}
// 用 ParameterizedThreadStart 產生 Thread
private void button3_Click(object sender, EventArgs e)
{
int num = 78;
ParameterizedThreadStart paramStart =
new ParameterizedThreadStart(count_param);
Thread thd = new Thread(paramStart);
fgDone = false;
guess = -1;
thd.Start(num);
timer2.Enabled = true;
}
private void timer1_Tick(object sender, EventArgs e)
{
if (fgDone)
{
timer1.Enabled = false;
textBox1.AppendText("計算完畢,sum= " +
sum.ToString()+"\r\n");
}
}
private void timer2_Tick(object sender, EventArgs e)
{
textBox1.AppendText(guess.ToString() + "\r\n");
if (fgDone)
{
timer2.Enabled = false;
textBox1.AppendText("找到了\r\n");
}
}
}
}
取得 thread 執行結果
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
Int32 sum;
public Form1()
{
InitializeComponent();
}
void count()
{
while (sum < int.MaxValue)
sum++;
}
void count_param(object param)
{
Int32[] pp = (Int32[])param;
while (pp[0] < int.MaxValue)
pp[0]++;
}
// 透過 sum 全域變數,儲存 thread 的執行結果
private void button1_Click(object sender, EventArgs e)
{
Thread thd = new Thread(count);
sum = 0;
textBox1.AppendText("執行緒開始執行\r\n");
thd.Start();
textBox1.AppendText("使用Join()方法,"+"" +
"所以必須等待執行緒執行結束...\r\n");
thd.Join();
textBox1.AppendText("sum=" + sum.ToString() +
"\r\n");
}
// 透過傳入 thread 的參數,儲存 thread 的執行結果
// 因為該參數是 array,傳入是 call by value
// 會將 sum1[0] 的記憶體位址傳給該 method
private void button2_Click(object sender, EventArgs e)
{
Thread thd = new Thread(count_param);
Int32 []sum1 = { 0 };
textBox1.AppendText("執行緒開始執行\r\n");
thd.Start(sum1);
textBox1.AppendText("使用Join()方法," + "" +
"所以必須等待執行緒執行結束...\r\n");
thd.Join();
textBox1.AppendText("sum1=" + sum1[0].ToString() +
"\r\n");
}
}
}
thread 生命週期
Thread 提供 Abort(), Join(), Interrupt(), Sleep(), Suspend(), Resume(),其中 Suspend(), Resume() 已經建議不要使用。
Join() 會等待 thread 結束,呼叫 Join() 的 thread 會持續等待無法回應。
結束 Thread 的方法,是讓該 Thread 完工後自行結束,如果是持續工作的 Thread,就要用 Abort(),或是用全域變數判斷要不要繼續執行。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
bool fgDone_a, fgDone_b;
int guess_a, guess_b;
Thread th_a=null;
bool fgRun;
int num_a = 78, num_b=50;
public Form1()
{
InitializeComponent();
}
// 亂數產生數字,直到該數字為 78
// 結束時,設定 fgDone_a,並停止 timer1
void count_a()
{
Random rd = new Random();
try
{
while (guess_a != num_a)
{
guess_a = rd.Next(1, 101);
Thread.Sleep(100);
}
fgDone_a = true;
}
catch (ThreadAbortException ex)
{
timer1.Enabled = false;
}
catch (ThreadInterruptedException ex)
{
timer1.Enabled = false;
}
}
void count_b()
{
Random rd = new Random();
while (guess_b != num_b && fgRun)
{
guess_b = rd.Next(1, 101);
Thread.Sleep(100);
}
fgDone_b = true;
}
private void timer1_Tick(object sender, EventArgs e)
{
textBox1.AppendText(guess_a.ToString() + "\r\n");
if (fgDone_a)
{
timer1.Enabled = false;
textBox1.AppendText("找到了\r\n");
}
}
// 用 count_a 產生 Thread th_a
// 同時啟動 timer1,timer1 會呼叫 timer1_Tick
private void button1_Click(object sender, EventArgs e)
{
Thread thd = new Thread(count_a);
th_a = thd;
fgDone_a = false;
guess_a = -1;
thd.Start();
timer1.Enabled = true;
}
// 呼叫 th_a.Abort()
private void button2_Click(object sender, EventArgs e)
{
textBox1.AppendText("使用Abort()中止執行緒\r\n");
th_a.Abort();
}
// 呼叫 th_a.Interrupt()
private void button3_Click(object sender, EventArgs e)
{
textBox1.AppendText("Interrupt()中斷執行緒\r\n");
th_a.Interrupt();
}
// 用 count_b 產生 Thread th_b
// 將全域變數 fgRun 設定為 true
// 同時啟動 timer2,timer2 會呼叫 timer2_Tick
private void button4_Click(object sender, EventArgs e)
{
Thread thd = new Thread(count_b);
fgDone_b = false;
guess_b = -1;
fgRun = true;
thd.Start();
timer2.Enabled = true;
}
// 將全域變數 fgRun 設定為 false
private void button5_Click(object sender, EventArgs e)
{
fgRun = false;
}
private void timer2_Tick(object sender, EventArgs e)
{
textBox2.AppendText(guess_b.ToString() + "\r\n");
if (fgDone_b)
{
timer2.Enabled = false;
if(!fgRun)
textBox2.AppendText("結束執行緒\r\n");
else
textBox2.AppendText("找到了\r\n");
}
}
// 關閉 Form1,要停止 th_a,fgRun 設定為 false
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
if(th_a!=null)
th_a.Abort();
fgRun = false;
}
}
}
Thead 存取表單控制項
Form UI 控制項是由 UI Thread 控制,如果自己產生的 Thread 要跨到 UI thread 存取控制項,會發生錯誤。
前面的例子,是在 Thread 裡面計算後,將過程記錄在全域變數中,然後在 UI Thread 以 Timer 定時將資料顯示在 UI 上,這種方法比較麻煩。
透過控制項的 InvokeRequired 屬性及 Invoke(),可讓 Thread 直接存取控制項。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
delegate void SafeCall(string str);
Thread []myThreads= { null, null };
int sum = 0;
public Form1()
{
InitializeComponent();
}
void safeControl() //無參數
{
if (textBox1.InvokeRequired)
{
sum++;
if (sum > int.MaxValue)
sum = 0;
MethodInvoker ivk = new MethodInvoker(safeControl);
textBox1.Invoke(ivk, new object[] { });
}
else
textBox1.AppendText("myThreads[1]: " +
sum.ToString() + "\r\n");
}
void myFunc()
{
while (true)
{
safeControl();
Thread.Sleep(500);
}
}
// 用 InvokeRequired 判斷 textBox1 存取權
// 如果是 true,就表示現在是 UI thread 在控制
// 就要由 textBox1 呼叫一次 SafeCall
// false 就表示為外部 thread 存取,可直接使用 textBox1
void safeControl_param(string str) //有參數
{
if (textBox1.InvokeRequired)
{
SafeCall ivk = new SafeCall(safeControl_param);
textBox1.Invoke(ivk, new object[] { str });
}
else
textBox1.AppendText(str + "\r\n");
}
// myThreads[0] 的 method
// 呼叫 safeControl_param
void myFunc_param()
{
Random rd = new Random();
int num;
while (true)
{
num = rd.Next(1, 101);
safeControl_param("myThreads[0]: " + num.ToString());
Thread.Sleep(500);
}
}
// 用 button1 產生 myThreads[0]
private void button1_Click(object sender, EventArgs e)
{
Thread thd;
// 如果 myThreads[0] 已經存在,就要停止 myThreads[0]
// 用 myFunc_param 重新產生一個 myThreads[0],並啟動
if (myThreads[0] != null)
{
myThreads[0].Abort();
myThreads[0].Join();
}
thd = new Thread(new ThreadStart(myFunc_param));
myThreads[0] = thd;
thd.Start();
}
// 用 button2 產生 myThreads[1]
private void button2_Click(object sender, EventArgs e)
{
Thread thd;
if (myThreads[1] != null)
{
myThreads[1].Abort();
myThreads[1].Join();
}
thd = new Thread(new ThreadStart(myFunc));
myThreads[1] = thd;
thd.Start();
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
foreach(var item in myThreads)
if (item != null)
item.Abort();
}
}
}
另一種寫法
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
Thread[] myThreads = { null, null };
int sum = 0;
public Form1()
{
InitializeComponent();
}
void safeControl_param(string str) //有參數
{
textBox1.Invoke(new Action (() =>
{
textBox1.AppendText(str);
}
));
}
// this.Invoke 配合 new Action
void myFunc_param()
{
Random rd = new Random();
int num;
while (true)
{
num = rd.Next(1, 101);
//也能寫成函式來來呼叫:safeControl_param(num.ToString());
this.Invoke(new Action(() =>
{
textBox1.AppendText("myThreads[0]: "+num.ToString()+"\r\n");
}
));
Thread.Sleep(500);
}
}
void safeControl() //無參數
{
sum++;
if (sum > int.MaxValue)
sum = 0;
this.Invoke((MethodInvoker)delegate
{ textBox1.AppendText( "myThreads[1]: " +
sum.ToString() + "\r\n"); }
);
}
void myFunc()
{
while (true)
{
////此段程式也能寫成函式來來呼叫:safeControl();
sum++;
if (sum > int.MaxValue)
sum = 0;
this.Invoke((MethodInvoker)delegate
{
textBox1.AppendText("myThreads[1]: " +
sum.ToString() + "\r\n");
}
);
Thread.Sleep(500);
}
}
// 有參數的 myFunc_param Thread
private void button1_Click(object sender, EventArgs e)
{
Thread thd;
if (myThreads[0] != null)
{
myThreads[0].Abort();
myThreads[0].Join();
}
thd = new Thread(new ThreadStart(myFunc_param));
myThreads[0] = thd;
thd.Start();
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
foreach (var item in myThreads)
if (item != null)
item.Abort();
}
// 沒有參數的 myFunc Thread
private void button2_Click(object sender, EventArgs e)
{
Thread thd;
if (myThreads[1] != null)
{
myThreads[1].Abort();
myThreads[1].Join();
}
thd = new Thread(new ThreadStart(myFunc));
myThreads[1] = thd;
thd.Start();
}
}
}
第三種寫法
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
delegate void SafeCall(string str);
Thread []myThread= { null, null };
int sum;
public Form1()
{
InitializeComponent();
}
void myFunc2()
{
MethodInvoker ivk = new MethodInvoker(safeControl);
while (true)
{
sum++;
if (sum > int.MaxValue)
sum = 0;
this.Invoke(ivk, new object[] { });
Thread.Sleep(500);
}
}
void safeControl() //無參數
{
textBox1.AppendText(sum.ToString() + "\r\n");
label1.Text = sum.ToString() ;
}
void myFunc1()
{
SafeCall ivk = new SafeCall(safeControl_param);
Random rd = new Random();
int num;
while (true)
{
num = rd.Next(1, 101);
this.Invoke(ivk, new Object[] { num.ToString()+"\r\n" });
Thread.Sleep(500);
}
}
private void safeControl_param(string str)
{
textBox1.AppendText(str);
label1.Text = str;
}
private void button1_Click(object sender, EventArgs e)
{
Thread thd = new Thread(myFunc1);
myThread[0] = thd;
thd.Start();
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
foreach (var item in myThread)
if (item != null)
item.Abort();
}
private void button2_Click(object sender, EventArgs e)
{
Thread thd = new Thread(myFunc2);
myThread[1] = thd;
sum = 0;
thd.Start();
}
}
}
執行緒同步
如果有多個 thread 會同時存取相同的資源,會造成內容一致性的問題。有四個方法,可處理 synchronization 問題
synchronized code region -> 最常用
當 thread A 執行時,thread B 必須等待 A 完成後,才能執行該區塊
manual synchronization
使用 .Net Framework 的 Mutex, Semaphore, EventWaitHandle, AutoResetEvent, ManualResetEvent
synchronized contexts
使用 SynchronizationAttribute 設定 ContextBoundObject
當物件進入或離開由 ContextBoundObject 定義的內容時,會強制執行規則
使用 System.Collections.Concurrent 的集合與類別
Critical Section: thread 存取的共用資源(物件、資料、變數、設備),同一時間只有一個 thread 能使用
lock()
互斥鎖: public object locker = new object();
lock(locker)
以 locker 鎖定 critical section
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
Thread thd1=null, thd2=null;
object locker = new object();
bool fg1 = false, fg2=false;
public Form1()
{
InitializeComponent();
}
// 當 Form1 載入時,就啟動兩個 thread
private void Form1_Load(object sender, EventArgs e)
{
thd1 = new Thread(func1);
thd2 = new Thread(func2);
thd1.Start();
thd2.Start();
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
if (thd1 != null)
thd1.Abort();
if (thd2 != null)
thd2.Abort();
}
// 無窮迴圈
// 當 fg1 為 true,就 lock locker 物件,執行 critical section 區塊
// 完成後將 fg1 改為 false
void func1()
{
while(true)
{
if(fg1)
lock(locker)
{
for(int i=0;i<10;i++)
{
textBox1.Invoke(new Action(() =>
{
textBox1.AppendText("Thread 1\r\n");
}));
Thread.Sleep(500);
}
fg1 = false;
}
}
}
void func2()
{
while (true)
{
if (fg2)
lock (locker)
{
textBox1.Invoke(new Action(() =>
{
textBox1.AppendText("Thread 2\r\n");
}));
fg2 = false;
}
}
}
// 將 fg1 改為 true
private void button1_Click(object sender, EventArgs e)
{
if (!fg1)
fg1 = true;
}
private void button2_Click(object sender, EventArgs e)
{
if (!fg2)
fg2 = true;
}
}
}
Monitor
method |
說明 |
Enter(a, [b]) |
取得並鎖定互斥鎖 a,a為 Object,b 為 Boolean。當 b 為 true,表示已經取得互斥鎖 a,否則為 false |
Exit(a) |
釋放互斥鎖 a |
IsEntered(a) |
判斷 thread 是否已經取得互斥鎖 a |
Pulse(a) |
通知等候的 thread,互斥鎖 a 已改變狀態 |
PulseAll(a) |
通知等候 queu 的所有 threads,互斥鎖 a 已改變狀態 |
TryEnter(a,b,c) |
嘗試在時間 b 取得互斥鎖 a,並回傳結果 c。 a: Object, b: Int32 or TimeSpan, c: Boolean |
TryEnter(a[,b]) |
嘗試在時間 b 取得互斥鎖 a。 a: Object, b: Int32 or TimeSpan |
TryEnter(a[,b]) |
嘗試取得互斥鎖 a,並回傳結果 b。 a: Object, b: Boolean |
Wait(a[,b[,c]]) |
釋放互斥鎖 a ,並在時間 b 內,嘗試重新取得互斥鎖 a。如果無法取得,就會進入等候 queue a: Object, b: Int32 or TimeSpan, c: Boolean |
Enter, Exit 要搭配使用,否則會造成 a 無法釋放或 deadlock
Pulse() 只能被正在鎖定互斥鎖的 thread 呼叫
critical section
Monitor.Enter(locker);
.
.
.
Monitor.Exit(locker);
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
Thread thd1 = null, thd2 = null;
object locker = new object();
bool fg1 = false, fg2 = false;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
thd1 = new Thread(func1);
thd2 = new Thread(func2);
thd1.Start();
thd2.Start();
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
if (thd1 != null)
thd1.Abort();
if (thd2 != null)
thd2.Abort();
}
void func1()
{
while (true)
{
if (fg1)
{
Monitor.Enter(locker);
for (int i = 0; i < 10; i++)
{
textBox1.Invoke(new Action(() =>
{
textBox1.AppendText("Thread 1\r\n");
}));
Thread.Sleep(500);
}
Monitor.Exit(locker);
fg1 = false;
}
}
}
void func2()
{
while (true)
{
if (fg2)
{
Monitor.Enter(locker);
textBox1.Invoke(new Action(() =>
{
textBox1.AppendText("Thread 2\r\n");
}));
Monitor.Exit(locker);
fg2 = false;
}
}
}
private void button1_Click(object sender, EventArgs e)
{
if (!fg1)
fg1 = true;
}
private void button2_Click(object sender, EventArgs e)
{
if (!fg2)
fg2 = true;
}
}
}
Semaphore
5 人去店裡繳費,但只有 3 個櫃檯
一般來說臨櫃劉成為:取號碼牌、有空的櫃檯時叫號、沒有空的櫃檯就等待
Semaphore 提供機制協調多個 thread 的同步處理
// 有三個號誌數量,最多可接受3個 thread 要求號誌
Semaphore smphore = new Semaphore(3,3);
// 有0個號誌數量,最多可接受3個 thread 要求號誌
// 一開始,所有 thread 都在等待
Semaphore smphore = new Semaphore(0,3);
// 釋放 3 個號誌
smphore.Release(3);
// 沒有參數,表示釋放先前取得的號誌
smphore.Release();
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
Semaphore smphore;
Thread[] thds=new Thread[5];
delegate void SafeCall(string str);
public Form1()
{
InitializeComponent();
}
void safeControl(string str)
{
textBox1.AppendText(str);
}
void func(object param)
{
int no = (int)param;
string str;
SafeCall ivk = new SafeCall(safeControl);
str = String.Format("第{0}位在排隊...\r\n", no);
textBox1.Invoke(ivk, new Object[] { str});
// 所有 threads 會停在這裡等待 semaphore
smphore.WaitOne();
str = String.Format("第{0}位正在繳費...\r\n", no);
textBox1.Invoke(ivk, new Object[] { str });
Thread.Sleep(1000);
str = String.Format("第{0}位繳費結束...\r\n", no);
textBox1.Invoke(ivk, new Object[] { str });
smphore.Release();
// 結束時,釋放 semaphore
}
private void Form1_Load(object sender, EventArgs e)
{
// 載入時,產生 semaphore
smphore = new Semaphore(0, 3);
}
private void button1_Click(object sender, EventArgs e)
{
// 產生並啟動 5 個 threads
for (int i = 0; i < thds.Length; i++)
thds[i] = new Thread(
new ParameterizedThreadStart(func));
for (int i = 0; i < thds.Length; i++)
thds[i].Start(i + 1);
textBox1.AppendText("尚未開始營業...\r\n");
Thread.Sleep(3000);
textBox1.AppendText("開始營業...\r\n");
smphore.Release(3);
}
}
}
References
博客來-C#程式設計從入門到專業(下):職場C#進階應用技術