WCF常见问题(2) -- Winform Host UI阻塞

在使用WCF的时候,用来启动服务的Host方式有:Console,WinForm,IIS,Windows Service 4种。其中Winform 作为Host的同时,通常出于某种业务需求,除了充当Host以外还有自己的UI显示与操作。在做这种应用的时候,常常会发现WCF的调用会导致UI阻塞,使得服务端的Winform无法正常操作。
下面来看一个示例:服务端UI自己每1s显示一条系统时间数据,客户端每5s调用WCF服务获取时间数据并显示。

 WCF常见问题(2) -- Winform Host UI阻塞_第1张图片
看看代码:
1. WCF, 服务实例使用 Single,  GetData 中模拟复杂操作,延迟了5s。
[ServiceContract] public interface IService1 { [OperationContract] string GetData(); } [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)] public class Service1 : IService1 { public string GetData() { // 模拟复杂操作,延迟5m Thread.Sleep(5000); return DateTime.Now.ToString("HH:mm:ss fff"); } } 

2. 服务端和客户端的代码。(为了简便,服务端和客户端通用一个Winform应用)
(1) 服务端(_host是ServiceHost的引用)
private void SetSever() { _host = new ServiceHost(typeof(WcfWinformHostLib.Service1)); _host.Open(); Text += "[Server]"; timer1.Interval = 1000; timer1.Tick += (s, e) => { var data = DateTime.Now.ToString("Server: HH:mm:ss.fff") + Environment.NewLine; textBox1.Text = data + textBox1.Text; }; }
(2)客户端(_client直接使用数据契约,通过ChannelFactory获取远程对象的引用)
private void SetClient() { var factory = new ChannelFactory<WcfWinformHostLib.IService1>( new NetTcpBinding(), "net.tcp://localhost:20001/WcfWinformHostLib/Service1"); _client = factory.CreateChannel(); Text += "[Client]"; timer1.Interval = 5000; timer1.Tick += (s, e) => { var data = "Client: " + _client.GetData() + Environment.NewLine; textBox1.Text = data + textBox1.Text; }; }
OK, 现在运行一下看看实际的运行情况:
1.启动服务端,这时候数据上看还是很正常的,每隔1s显示一条数据。
WCF常见问题(2) -- Winform Host UI阻塞_第2张图片WCF常见问题(2) -- Winform Host UI阻塞_第3张图片
2.启动客户端,也是正常的每5s一条数据。
WCF常见问题(2) -- Winform Host UI阻塞_第4张图片WCF常见问题(2) -- Winform Host UI阻塞_第5张图片
再看看服务端,这个时候数据已经不正常了,变成和客户端一下的5s一次了,而且拖拽服务端界面还觉得很卡,UI明显有阻塞。
WCF常见问题(2) -- Winform Host UI阻塞_第6张图片

开始找原因,第一眼你可能会觉得是否是 Service 的 InstanceContextMode = Single 的问题,实际证明就算修改为 PerCall 也不会任何改变,那会不会是重入模式设置的不正确呢?不是,修改为 ConcurrencyMode.Multiple 服务端应用的UI依然阻塞。经过多次调试,发现这个UI阻塞的罪魁祸首是 ServiceHost,猜想 ServiceHost 一些启动的监听操作会和 UI 的消息循环产生冲突。(谁知道具体的原因,请留言告知,多谢)。我们修改一下 ServiceHost 把它扔到一个线程里:

添加一个 ThreadServiceHost 类

public class ThreadServiceHost { const int SleepTime = 100; private ServiceHost _serviceHost = null; private Thread _thread; private bool _isRunning; public ThreadServiceHost(Type serviceType) { _serviceHost = new ServiceHost(serviceType); _thread = new Thread(RunService); _thread.Start(); } void RunService() { try { _isRunning = true; _serviceHost.Open(); while (_isRunning) { Thread.Sleep(SleepTime); } _serviceHost.Close(); ((IDisposable)_serviceHost).Dispose(); } catch (Exception) { if (_serviceHost != null) _serviceHost.Close(); } } public void Stop() { lock (this) { _isRunning = false; } } } 
同时,修改服务端的启动代码:
_host = new ThreadServiceHost(typeof(WcfWinformHostLib.Service1));
 
再次启动服务端和客户端,这下运行正常了。
服务端:每 1s 一条数据, 客户端:每 5s 一条数据。
WCF常见问题(2) -- Winform Host UI阻塞_第7张图片

你可能感兴趣的:(thread,UI,timer,WinForm,WCF,textbox)