【问题标题】:Do events work if raised asynchronously?如果异步引发事件会起作用吗?
【发布时间】:2010-11-20 23:45:09
【问题描述】:

我有一个类的以下骨架。正如您在 TODO: 注释中看到的那样,我将在这里实现一个 AsyncEnumerator 构造。此方法将获取请求并将数据传递给另一个要处理的方法。根据我想调用事件的过程,SendMilestoneReached 或 SendFailed。由于 AsyncEnumerator,我担心这些可能会发生在不同的线程上。

这会对调用 Webtext 类的 UI 线程产生影响吗?

/// <summary>
/// Sends Webtexts.
/// </summary>
public class Webtext
{
    #region Event Definitions

    // Events.
    public event EventHandler<SendingEventArgs> SendStarted = delegate { };
    public event EventHandler<SendingEventArgs> SendFailed = delegate { };
    public event EventHandler<SendingEventArgs> SendSuccessful = delegate { };
    public event EventHandler<SendingEventArgs> SendMilestoneReached = delegate { };

    // Shared EventArgs Object, Consumed by the Events.
    SendingEventArgs EventArgs = new SendingEventArgs();

    #endregion

    /// <summary>
    /// Executes the send request.
    /// </summary>
    /// <param name="Operator">The operator whos service to use.</param>
    /// <param name="Username">The username of the requested operator.</param>
    /// <param name="Password">The password of the requested operator.</param>
    /// <param name="Content">The content to send.</param>
    /// <param name="Recipient">The recipient to recieve the content.</param>
    public void ExecuteSendRequest(string Operator, 
                                   string Username, 
                                   string Password, 
                                   string Content, 
                                   string Recipient)
    {
        //TODO: Implement Async requests here.
    }

    #region Event Handlers

    /// <summary>
    /// Called when [sending started].
    /// </summary>
    protected void OnSendingStarted()
    {
        SendStarted(this, EventArgs);
    }

    /// <summary>
    /// Called when [send fail].
    /// </summary>
    protected void OnSendFail()
    {
        SendFailed(this, EventArgs);
    }

    /// <summary>
    /// Called when [send successful].
    /// </summary>
    protected void OnSendSuccessful()
    {

        SendSuccessful(this, EventArgs);
    }

    /// <summary>
    /// Called when [send milestone reached].
    /// </summary>
    protected void OnSendMilestoneReached()
    {
        SendMilestoneReached(this, EventArgs);
    }

    #endregion


}

【问题讨论】:

    标签: c# events asynchronous thread-safety


    【解决方案1】:

    事件由引发它的同一 Thread 创建。这个原则听起来很简单,但很重要。

    所以:

    场景 1 应用已打开。 Webtext 由 UI 线程中的表单初始化,并调用其发送。 Webtext同步发送请求并触发事件。在整个过程中,所有操作都在 UI 线程上完成。

    场景 2 应用已打开。 Webtext 由 UI 线程中的表单初始化,并调用其发送。 Webtext 使用工作线程异步发送请求。第二个线程在完成时触发事件。这将是工作线程(后台或前台取决于您创建线程的方式)。通过此线程对 UI 元素的任何调用都需要使用 Invoke 完成。

    如您所见,这在很大程度上取决于您如何实现 send 方法。我看不到 send 本身的任何实现,所以我只能说您是生成线程还是使用线程池它将在工作线程上,否则简单地同步发送将在 UI 线程上。

    【讨论】:

    • 虽然我没有包含实现,但我认为您的第二种情况是适用的。本质上,我将使用 webrequest 对象并异步调用它。它由第三方库处理,它允许我按顺序编写调用,但它仍然在另一个线程上触发。您是否有关于如何使用调用将事件触发回 UI 线程的链接。
    • 您可以通过“Invoke UI Thread C#”搜索它。这个很短很简单:blogs.msdn.com/b/csharpfaq/archive/2004/03/17/91685.aspx
    • 我在想这个:stackoverflow.com/questions/1698889/…,因为它看起来更有活力。
    • 是的。这更好/更优雅,但基础是一样的。
    【解决方案2】:

    请注意,如果您异步引发事件并且处理程序需要更新用户界面,则它们需要与 UI 线程同步以更新任何用户控件。如果你异步引发事件,你会给订阅你的事件的类带来更大的负担:它们必须知道如何进行 UI 线程同步。

    对我来说,总的来说,我发现异步事件不值得麻烦,因为我几乎总是不得不在 UI 程序中处理这些事件。我得出的结论是,如果我想要异步处理事件,那么处理程序应该负责处理。

    这不是硬性规定。无论如何都不是。例如,System.Timers.Timer 默认在 ThreadPool 线程上引发事件,但您可以指定 SynchronizingObject 以便它可以与 UI 线程同步。

    如果您决定使用异步事件,那么我建议您包含一个工具,例如 Timer 的 SynchronizingObject,这样 UI 客户端就可以使用您的类,而不必处理 UI 线程同步的复杂性。

    【讨论】:

    • 我别无选择,我正在使用 WP7,所以我的应用程序需要的大多数调用都是异步的。
    • 我将如何同步我将要使用的线程。 Dispatcher.BeginInvoke( () => { // 此代码在 UI 线程上。});由于我上面链接中的很多花哨的选项不适用于 wp7
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-29
    • 1970-01-01
    • 1970-01-01
    • 2013-07-22
    • 2014-09-29
    • 1970-01-01
    相关资源
    最近更新 更多