启动表单的WCF回调使表单冻结

时间:2016-02-19 01:21:19

标签: c# multithreading wcf thread-safety

最终目标是拥有一个调用回调函数的WCF服务器,每个订阅的WCF客户端在回调上显示一个新的表单实例。

为了实现这一点,我使用pub / sub模式,客户端通过命名管道连接到服务器并调用Subscribe(),然后在从事件日志中读取事件时接收回调,EventReceived() 。在回调的实现中,客户端实例化表单的新实例并显示它。

问题是客户端显示的表单会立即冻结。任何的意见都将会有帮助。下面显示了具有相同行为的代码的简化版本。

客户端代码(Form1只是一个空白的默认表单,没有其他代码/装饰器.Form2的唯一目的是记录SynchronizationContext.Current以供稍后使用的回调。)

static class Program
{
    public static SynchronizationContext SynchronizationContext;

    [STAThread]
    static void Main()
    {
        ClientContract callbacks = new ClientContract();

        DuplexChannelFactory<IEventService> pipeFactory = new DuplexChannelFactory<IEventService>(
            callbacks, new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/EventPipe"));

        IEventService pipeProxy = pipeFactory.CreateChannel();
        pipeProxy.Subscribe();


        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form2());
    }
}

[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
class ClientContract : IClientContract
{
    public void EventReceived(int eventId)
    {
        Form1 newForm = new Form1();
        newForm.Show();
    }
}

客户端代码 - Form2构造函数

public Form2()
{
    InitializeComponent();
    Program.SynchronizationContext = SynchronizationContext.Current;
}

服务器代码

class Program
{
    static void Main(string[] args)
    {
        using (ServiceHost host = new ServiceHost(typeof(EventService), new Uri[] { new Uri("net.pipe://localhost") }))
        {
            host.AddServiceEndpoint(typeof(IEventService), new NetNamedPipeBinding(), "EventPipe");
            host.Open();
            Console.WriteLine("Service started");

            do
            {
                Console.WriteLine("Enter \"callback\" to trigger a callback, or enter to quit");
                string input = Console.ReadLine();

                if (input == "callback")
                {
                    EventService.PublishEvent(3);
                }
                else if (input == "")
                {
                    break;
                }
            } while (true);

            host.Close();
        }
    }
}

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single, InstanceContextMode = InstanceContextMode.PerCall)]
class EventService : IEventService
{
    public static event NewEventHandler NewEventAvailableHandler;
    public delegate void NewEventHandler(EventLogEventArgs e);

    private NewEventHandler newEventHandler;
    private IClientContract callback = null;

    public static void PublishEvent(int eventId)
    {
        EventLogEventArgs e = new EventLogEventArgs();
        e.eventId = eventId;

        if (NewEventAvailableHandler != null)
        {
            NewEventAvailableHandler(e);
        }
    }

    public void Subscribe()
    {
        callback = OperationContext.Current.GetCallbackChannel<IClientContract>();
        newEventHandler = new NewEventHandler(MagazineService_NewIssueAvailableEvent);
        NewEventAvailableHandler += newEventHandler;
    }

    public void Unsubscribe()
    {
        NewEventAvailableHandler -= newEventHandler;
    }

    public void MagazineService_NewIssueAvailableEvent(EventLogEventArgs e)
    {
        callback.EventReceived(e.eventId);
    }
}

public class EventLogEventArgs : EventArgs
{
    public int eventId;
}

共享代码(通过类库DLL文件实现)

[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IClientContract))]
public interface IEventService
{
    [OperationContract(IsOneWay = true, IsInitiating = true)]
    void Subscribe();

    [OperationContract(IsOneWay = true, IsInitiating = true)]
    void Unsubscribe();
}

public interface IClientContract
{
    [OperationContract(IsOneWay = true)]
    void EventReceived(int eventId);
}

** 修改 **

正如评论中所指出的,回调是在工作线程上收到的,但UI事件需要在主线程上发生。我能够让它工作,但感觉就像一个黑客,因为我有一个未使用的第二个表单,只是为了使用回调的SynchronizationContext。

0 个答案:

没有答案