Silverlight 強制異步調用 WCF (Begin/End 模式),導致複雜業務流程陷入「回調地獄」。本文介紹 DanielVaughan.SynchronousChannelBroker 庫,通過線程池 + 同步上下文切換,實現代碼邏輯同步寫法、底層異步執行,附完整集成步驟、代碼對比、注意事項。
// 原生異步:多步驟業務極其痛苦
void Step1() { client.DoWorkAsync(input1); }
void Client_DoWorkCompleted(object sender, DoWorkCompletedEventArgs e)
{
if (e.Error != null) return;
Step2(e.Result); // 再註冊下一個 Completed...
}
void Step2(string input2) { client.DoWork2Async(input2); }
// ... 無盡鏈條
DanielVaughan.dll (或源碼編譯)using DanielVaughan;
using System.Threading;
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
// 必須初始化同步上下文 (UI 線程)
UISynchronizationContext.Instance.Initialize(Dispatcher);
}
private void btnSync_Click(object sender, RoutedEventArgs e)
{
string input = txtInput.Text;
string result = "NULL";
// 關鍵:放入線程池執行同步調用,避免阻塞 UI 線程
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
// 獲取通道 (需實現 ChannelManager 單例)
var svc = ChannelManager.Instance.GetChannel();
// 核心:同步調用,內部自動處理 Begin/End + 異常傳播
result = SynchronousChannelBroker.PerformAction(
svc.BeginDoWork,
svc.EndDoWork,
input);
}
catch (Exception ex)
{
result = "Error: " + ex.Message;
}
// 切回 UI 線程更新界面
Dispatcher.BeginInvoke(() =>
{
txtResult.Text += $"\r\n同步調用--{result}\r\n";
});
});
}
// 原生異步對比
private void btnAsync_Click(object sender, RoutedEventArgs e)
{
var client = new Service1Client();
client.DoWorkCompleted += (s, args) =>
{
txtResult.Text += $"異步調用--{args.Result}\r\n";
};
client.DoWorkAsync(txtInput.Text);
}
}
public class ChannelManager
{
private static readonly Lazy _instance =
new Lazy(() => new ChannelManager());
public static ChannelManager Instance => _instance.Value;
private ChannelFactory _factory;
private ChannelManager()
{
var binding = new BasicHttpBinding();
var endpoint = new EndpointAddress("Service1.svc");
_factory = new ChannelFactory(binding, endpoint);
}
public T GetChannel() where T : class
{
return _factory.CreateChannel();
}
}
[ServiceContract]
public interface IService1
{
[OperationContract]
string DoWork(string input);
}
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Service1 : IService1
{
public string DoWork(string input)
{
Thread.Sleep(1000); // 模擬耗時操作
return $"服務端處理完成,返回: {input} @ {DateTime.Now}";
}
}
| 組件 | 作用 |
|---|---|
| UISynchronizationContext | 捕獲 UI 線程 SynchronizationContext,保證回調能切回 UI 更新控件 |
| ThreadPool.QueueUserWorkItem | 將同步調用移至後台線程,避免凍結 UI |
| SynchronousChannelBroker.PerformAction | 內部用 AutoResetEvent 阻塞線程,等待 Begin/End 完成,自動處理異常、超時、取消 |
| ChannelManager | 複用 ChannelFactory,避免頻繁創建開銷,單例線程安全 |
DanielVaughan 庫通過「線程池阻塞 + 同步上下文切換」巧妙破解 Silverlight 強制異步限制,讓開發者用同步思維編寫複雜 WCF 調用鏈。雖然技術棧已過時,但其同步包裝異步模式 (Sync-over-Async) 在現代 .NET 中依然廣泛應用 (如 Task.Run、AsyncContext、Nito.AsyncEx),理解其原理極具價值。