Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 启动表单的WCF回调使该表单冻结_C#_Multithreading_Wcf_Thread Safety - Fatal编程技术网

C# 启动表单的WCF回调使该表单冻结

C# 启动表单的WCF回调使该表单冻结,c#,multithreading,wcf,thread-safety,C#,Multithreading,Wcf,Thread Safety,最终目标是拥有一个调用回调函数的WCF服务器,每个订阅的WCF客户机在回调上显示一个新的表单实例 为了实现这一点,我使用了一种pub/sub模式,其中客户端通过命名管道连接到服务器,并调用Subscribe(),然后在从事件日志读取事件时接收回调EventReceived()。在回调的实现中,客户端实例化一个表单的新实例并显示它 问题是客户端显示的表单会立即冻结。任何建议都会有帮助。下面是展示相同行为的代码的简化版本 客户端代码(Form1只是一个空白的默认表单,没有额外的代码/修饰符。Form

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

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

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

客户端代码(Form1只是一个空白的默认表单,没有额外的代码/修饰符。Form2的唯一用途是记录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;
}
**编辑**


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

如果在
Form1 newForm=new Form1()上设置断点内部
EventReceived(int-eventId)
,它是什么线程?主/GUI线程?它看起来像一个工作线程(请参阅)。主线程在Main()中的Application.Run()上阻塞,我没有做任何显式的操作来创建新线程(所有代码都在原始文章中)。好的。它是冻结的,因为您无法从工作线程创建/更新UI。让您的回调在应用程序的主窗口上执行
BeginInvoke
或其他操作,以代表您显示表单。实际上,当您在工作线程中执行时,您无法更新在另一个线程中创建的现有UI元素。但您可以创建一个全新的表单——它将有自己的调度程序(当我们在WPF世界中时),这几乎不意味着它自己的Windows消息处理。更多信息:虽然这是可能的,但不建议在应用程序中出现这种情况,您最好将创建新窗口的请求发回主UI线程。谢谢你们的建议。我能够让它工作并更新帖子,但我这样做感觉像是一个黑客。我正在使用第二个表单,我甚至不需要它,只是为了在Application.Run()中获取SynchronizationContext.Current,只是为了稍后回调使用它来显示我关心的表单。肯定有更好的办法吗?
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;
}
[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);
}