多執行緒共同存取共享資料時,必須同步化避免競爭條件。本文以「兩條執行緒共同從 ArrayList 移除元素」為例,示範 Monitor 鎖定、Event 通知完成、Thread.Abort 停止 (含現代替代方案) 的完整流程。
using System;
using System.Collections;
using System.Threading;
public class ThreadSyncDemo
{
private Thread _thread1;
private Thread _thread2;
private ArrayList _sharedList;
private event EventHandler _onCompleted; // 完成事件
public ThreadSyncDemo(int itemCount)
{
// 初始化資料
_sharedList = new ArrayList(itemCount);
var rnd = new Random(1000000);
for (int i = 0; i < itemCount; i++)
_sharedList.Add(rnd.Next().ToString());
_thread1 = new Thread(Worker) { Name = "執行緒-1" };
_thread2 = new Thread(Worker) { Name = "執行緒-2" };
_onCompleted += OnCompletedHandler;
}
public void Start()
{
_thread1.Start();
_thread2.Start();
}
private void Worker()
{
string item = null;
while (true)
{
Monitor.Enter(this); // 進入關鍵區段
try
{
if (_sharedList.Count == 0) break; // 雙重檢查
item = (string)_sharedList[0];
Console.WriteLine($"{Thread.CurrentThread.Name} 移除: {item}");
_sharedList.RemoveAt(0);
if (_sharedList.Count == 0)
_onCompleted?.Invoke(this, EventArgs.Empty); // 觸發完成事件
}
finally
{
Monitor.Exit(this); // 確保釋放鎖
}
Thread.Sleep(5); // 模擬處理延遲
}
}
private void OnCompletedHandler(object sender, EventArgs e)
{
Console.WriteLine("所有項目處理完成,停止所有執行緒。");
// 注意:Abort 已過時,現代做法見下方
_thread2.Abort();
_thread1.Abort();
}
}
// 使用
class Program
{
static void Main()
{
var demo = new ThreadSyncDemo(1000);
demo.Start();
Console.ReadLine();
}
}
Monitor.Enter(this) 以目前實例為鎖物件,確保同一時間只有一條執行緒執行關鍵區段。finally 中呼叫 Monitor.Exit**,否則例外導致鎖永不釋放造成死結。lock (this) { ... },但 Monitor 可搭配 TryEnter(timeout) 避免永久阻塞。
_onCompleted += OnCompletedHandler; // 訂閱
_onCompleted?.Invoke(this, EventArgs.Empty); // 發布 (C# 6+ null 條件運算子)
+=/-=,符合封裝原則。Thread.Abort 會在目標執行緒擲出 ThreadAbortException,導致:
PlatformNotSupportedException。
public class ModernThreadSyncDemo
{
private CancellationTokenSource _cts = new CancellationTokenSource();
private List _sharedList = new List();
private readonly object _lock = new object();
public void Start(int count)
{
// 初始化資料...
var t1 = Task.Run(() => Worker("Worker-1"), _cts.Token);
var t2 = Task.Run(() => Worker("Worker-2"), _cts.Token);
// 可選:等待完成
Task.WhenAll(t1, t2).ContinueWith(_ => Console.WriteLine("全部完成"));
}
private void Worker(string name)
{
while (true)
{
string item = null;
lock (_lock)
{
if (_sharedList.Count == 0) break;
item = _sharedList[0];
_sharedList.RemoveAt(0);
}
// 模擬工作
Thread.Sleep(5);
// 定期檢查取消
if (_cts.Token.IsCancellationRequested) break;
}
}
public void Cancel() => _cts.Cancel();
}
CancellationToken 協作式取消,執行緒自行檢查並優雅結束。Task/Task.Run,無需手動管理 Thread 物件。ThrowIfCancellationRequested 可在等待點 (如 Task.Delay) 直接拋出 OperationCanceledException。| 原語 | 用途 | 關鍵方法 |
|---|---|---|
lock / Monitor | 互斥存取關鍵區段 | Enter/Exit, TryEnter |
Mutex | 跨進程同步 | WaitOne, ReleaseMutex |
Semaphore/Slim | 限制並發數量 | Wait, Release |
AutoResetEvent | 單一執行緒通知 | Set, WaitOne |
ManualResetEvent/Slim | 廣播通知多執行緒 | Set, Reset, WaitOne |
CountdownEvent | 等待 N 個信號 | Signal, Wait |
Barrier | 多執行緒同步點 | SignalAndWait |
ReaderWriterLockSlim | 讀多寫少優化 | EnterReadLock, EnterWriteLock |
Interlocked | 原子操作 (計數、交換) | Increment, CompareExchange |
lock/Monitor),並用 finally 確保釋放。TaskCompletionSource,避免輪詢。CancellationToken 協作式取消**,拒絕 Thread.Abort。Task/async-await、System.Threading.Channels、BlockingCollection 等高階抽象,減少手動同步複雜度。