當第三方程式庫僅提供非同步 API (如 XMPP 連線庫),但 ASP.NET 頁面生命週期為同步模式時,需將非同步作業同步化:頁面呈現前必須完成回呼驗證。
XmppClientConnection.Open() 立即返回,登入/註冊在背景執行緒進行,成功觸發回呼事件。若直接呼叫,頁面會先渲染,回呼才稍後執行,違反業務邏輯。
private object objlock = new object();
public void RegisterJab(...)
{
Monitor.Enter(objlock);
_connection.Open();
Monitor.Wait(objlock); // 等待回呼釋放
_connection.Close();
}
private void XmppCon_OnRegistered(object sender)
{
IsSuccessfull = true;
Monitor.Exit(objlock); // 拋出 SynchronizationLockException!
}
失敗原因:回呼在不同執行緒 (Jabber 輔助執行緒),非鎖定擁有者,無法呼叫 Monitor.Exit。
AutoResetEvent 專為執行緒間訊號通知設計,允許非擁有者執行緒發送訊號。
public class Register
{
private XmppClientConnection _connection;
private static AutoResetEvent _resetEvent;
public bool IsSuccessfull = false;
public bool IsUsed = false;
public Register()
{
_connection = new XmppClientConnection();
_connection.OnLogin += XmppCon_OnLogin;
_connection.OnRegisterError += XmppCon_OnRegErr;
_connection.OnRegistered += XmppCon_OnRegistered;
}
public void RegisterJab(string username, string password, string server)
{
// 設定連線參數...
_resetEvent = new AutoResetEvent(false); // 非終止狀態
_connection.Open();
// 阻塞 ASP.NET 執行緒,最多等待 20 秒
_resetEvent.WaitOne(20 * 1000, true);
_connection.Close();
}
private void XmppCon_OnRegistered(object sender)
{
IsSuccessfull = true;
_resetEvent.Set(); // 發送訊號,喚醒主執行緒
}
private void XmppCon_OnLogin(object sender)
{
IsSuccessfull = true;
_resetEvent.Set();
}
private void XmppCon_OnRegErr(object sender, Element e)
{
IsSuccessfull = false;
if (e.SelectSingleElement("error").Attribute("code") == "409")
IsUsed = true; // 用戶已存在
_resetEvent.Set();
}
}
| 機制 | Monitor | Mutex | AutoResetEvent |
|---|---|---|---|
| 跨執行緒釋放 | ❌ 僅擁有者 | ✅ 可跨執行緒 | ✅ 專為訊號設計 |
| 語意 | 互斥鎖 | 互斥體 | 事件訊號 |
| 適用場景 | 關鍵區段 | 跨進程同步 | 執行緒協調/等待 |
new AutoResetEvent(false) - 非受信狀態WaitOne(timeout) 阻塞等待Set() 釋放此模式完美解決「非同步庫同步化」需求,確保業務邏輯順序正確。