events/delegates 调用时如何使用 EndInvoke 不是你的责任

How to use EndInvoke when events/delegates called are not your responsibility

我目前有一个 class,不断从 API 接收信息。 当它收到该信息时,它会触发一个 event/delegate,其他 classes 可以订阅。

因为我不想阻塞线程 class 是 运行 我使用 delegate.BeginInvoke 来触发事件。

例如

    object UpdateLock=new object();
    List<Action<object, UpdateArgs>> UpdateSubscribersList = new List<Action<object, UpdateArgs>>();
    public void UpdateSubscribe(Action<object, UpdateArgs> action)
    {
        lock (UpdateLock)
        {
            UpdateSubscribersList.Add(action);
        }
    }
    public bool UpdateUnSubscribe(Action<object, UpdatePortfolioArgs> action)
    {
        lock (UpdateLock)
        {
            return UpdateSubscribersList.Remove(action);
        }
    }
    public virtual void onUpdate(UpdateArgs args)
    {
        lock (UpdateLock)
        {
            foreach (Action<object, UpdateArgs> action in UpdateSubscribersList)
            {
                action.BeginInvoke(args, null, null);
            }
        }
    }

这只是一个例子。不要担心使用列表而不是多播委托 - 还有一些其他代码导致我使用它。

无论如何我有两个问题:

1) 我应该把 EndInvoke 放在哪里才不会阻塞线程?我相信这个问题已经有人问过了,但我不是很清楚,反正这不是我的主要问题。

2)EndInvoke的主要作用是清理线程和处理异常。现在清理线程就好了,但是我该如何处理异常呢?我不知道这些委托会调用哪些代码,而且该代码不是我的责任。那么无论如何我可以让这些事件的订阅者必须处理清理代码和异常,而不是我的 class?还是无论如何他都可以访问 EndInvoke 返回的任何内容,以便他可以处理任何异常?

我认为这个模式应该适合你。

public void StartAsync(Action a)
{
    a.BeginInvoke(CallBack, a); // Pass a Callback and the action itself as state

    // or a.BeginInvoke(Callback, null); then see alternative in Callback
}

private void CallBack(IAsyncResult ar)
{
    // In the callback you can get the delegate
    Action a = ar.AsyncState as Action;

    // or
    // Action a = ((AsyncCallback)ar).AsyncDelegate as Action

    try
    {
        // and call EndInvoke on it.
        a?.EndInvoke(ar); // Exceptions will be re-thrown when calling EndInvoke
    }
    catch( Exception ex )
    {
        Log.Error( ex, "Exception in onUpdate-Action!" );
    }
}

您应该能够根据您的 Action<object, UpdateArgs> 代表进行调整。

我使用 StartAsync 只是为了简洁。当然,这将在您的 onUpdate 方法的 for 循环内。

请注意 Callback 将在异步线程上调用!如果您想在此处更新 GUI 元素,则需要将其编组回 GUI 线程。


如果您将活动添加到 class

public event EventHandler<Exception> OnError;

您可以发布这些例外情况:

catch( Exception ex )
{
    Log.Error( ex, "Exception in onUpdate-Action!" );
    OnError?.Invoke( a, ex );
}