【问题标题】:Can an asynchronously fired event run synchronously on a form?异步触发的事件可以在表单上同步运行吗?
【发布时间】:2010-05-17 16:26:33
【问题描述】:

[带有 .Net Framework 3.5 的 VS 2010 Beta]

我编写了一个 C# 组件来异步监视套接字并在接收到数据时引发事件。我将 VB 表单设置为在引发事件时显示消息框。我注意到的是,当组件同步引发事件时,消息框会阻塞组件代码并锁定表单,直到用户关闭消息。当它被异步引发时,它既不会阻塞代码,也不会锁定表单。

我想要的是一种引发事件的方法,它不会阻塞代码,而是在与表单相同的线程上调用(这样它会锁定表单,直到用户选择一个选项。)

你能帮帮我吗? 谢谢。

[组件]

using System;
using System.Threading;
using System.ComponentModel;

namespace mySpace
{
    public delegate void SyncEventHandler(object sender, SyncEventArgs e);
    public delegate void AsyncEventHandler(object sender, AsyncEventArgs e);

    public class myClass
    {
        readonly object syncEventLock = new object();
        readonly object asyncEventLock = new object();

        SyncEventHandler syncEvent;
        AsyncEventHandler asyncEvent;

        private delegate void WorkerDelegate(string strParam, int intParam);

        public void DoWork(string strParam, int intParam)
        {
            OnSyncEvent(new SyncEventArgs());
            AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(null);
            WorkerDelegate delWorker = new WorkerDelegate(ClientWorker);
            IAsyncResult result = delWorker.BeginInvoke(strParam, intParam, null, null);
        }

        private void ClientWorker(string strParam, int intParam)
        {
            Thread.Sleep(2000);
            OnAsyncEvent(new AsyncEventArgs());
            OnAsyncEvent(new AsyncEventArgs());
        }

        public event SyncEventHandler SyncEvent
        {
            add { lock (syncEventLock) syncEvent += value; }
            remove { lock (syncEventLock) syncEvent -= value; }
        }
        public event AsyncEventHandler AsyncEvent
        {
            add { lock (asyncEventLock) asyncEvent += value; }
            remove { lock (asyncEventLock) asyncEvent -= value; }
        }

        protected void OnSyncEvent(SyncEventArgs e)
        {
            SyncEventHandler handler;
            lock (syncEventLock) handler = syncEvent;
            if (handler != null) handler(this, e, null, null); // Blocks and locks
            //if (handler != null) handler.BeginInvoke(this, e, null, null); // Neither blocks nor locks
        }
        protected void OnAsyncEvent(AsyncEventArgs e)
        {
            AsyncEventHandler handler;
            lock (asyncEventLock) handler = asyncEvent;
            //if (handler != null) handler(this, e, null, null); // Blocks and locks
            if (handler != null) handler.BeginInvoke(this, e, null, null); // Neither blocks nor locks
        }
    }
}

[表格]

Imports mySpace

Public Class Form1

    Public WithEvents component As New mySpace.myClass()

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        component.DoWork("String", 1)
    End Sub

    Private Sub component_SyncEvent(ByVal sender As Object, ByVal e As pbxapi.SyncEventArgs) Handles component.SyncEvent
        MessageBox.Show("Synchronous event", "Raised:", MessageBoxButtons.OK)
    End Sub

    Private Sub component_AsyncEvent(ByVal sender As Object, ByVal e As pbxapi.AsyncEventArgs) Handles component.AsyncEvent
        MessageBox.Show("Asynchronous event", "Raised:", MessageBoxButtons.OK)
    End Sub
End Class

【问题讨论】:

  • 如果您拨打Delegate.BeginInvoke,您也必须拨打EndInvoke

标签: c# .net vb.net asynchronous delegates


【解决方案1】:

您需要调用 formBeginInvoke 方法(仅),该方法将在表单的 UI 线程上运行委托(从而阻塞表单),而不会阻塞调用线程等待让通话结束。

如果您没有对表单实例的引用,您可以从 UI 线程中保存 SynchronizationContext.Current,然后在 SynchronizationContext 实例上调用 Post,这将是等效的。

【讨论】:

  • 既然我理解了他的要求,我相信你是对的。 +1
  • 好吧,我想我明白为什么会发生这种情况,但我对如何让它发挥作用有点困惑。我在哪里保存 UI 线程的上下文,如何将它传递给我的异步线程?
  • 从 UI 线程将其保存到类中的字段中(可能在 DoWork 中)
【解决方案2】:

我建议使用SynchronizationContext.Post,在委托中传递对话框代码。这不会阻塞(其他线程的)代码,而是会在UI线程中执行对话框代码,所以会被阻塞。

【讨论】:

    【解决方案3】:

    是的,您可以使用表单(或任何控件)上的Invoke() 方法在 UI 线程中以同步的方式同步和执行任何委托。

    【讨论】:

    • 我认为他不是这个意思。他想要一种简单的方法来“锁定”表单,但他仍然希望他的 UI 线程能够泵送消息。反正我就是这么读他的问题的。
    • Invoke会阻塞组件代码,我认为OP需要使用BeginInvoke在UI线程上执行而不阻塞组件代码。
    • 我尝试过使用 Invoke() 而不是 BeginInvoke(),它适用于表单,但它会阻止组件代码。此外,在使用 BeginInvoke() 调用的方法中调用 Invoke() 不会改变任何内容。 (在哪里可以找到帖子和 cmets 的格式指南?)
    • @cyclotis04 - 你可以在这里找到一些格式化语法stackoverflow.com/editing-help
    【解决方案4】:

    如果我理解正确,我认为你以错误的方式处理问题。

    我认为您需要在显示消息框时明确禁用表单上的控件,然后在用户关闭该框时重新启用它们。

    请澄清;您是否希望 UI 线程在显示 Box 时泵送消息? (例如,表单会自行重绘)

    【讨论】:

    • 你错了。他需要在 UI 线程上调用ShowDialog
    • 他希望它发送消息,但不响应用户输入。
    • 如果我在不使用任何 BeginInvoke() 的情况下引发事件,则消息框会锁定表单,直到用户做出选择。如果涉及任何 BeginInvoke(),则表单不会锁定。
    • @cyclotis04 - 我确信 SLaks 是正确的。关键是您在 VB 表单上调用 BeginInvoke。
    猜你喜欢
    • 2010-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多