【问题标题】:How to use EndInvoke when events/delegates called are not your responsibility当调用的事件/委托不是您的责任时如何使用 EndInvoke
【发布时间】:2017-09-06 09:59:53
【问题描述】:

我目前有一个不断从 API 接收信息的类。 当它收到该信息时,它会触发其他类可以订阅的事件/委托。

因为我不想阻塞运行该类的线程,所以我使用 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 的主要目的是清理线程和处理异常。现在清理线程很好,但是我该如何处理异常呢?我不知道这些代表将调用哪些代码,并且该代码不是我的责任。那么无论如何我可以让这些事件的订阅者不得不处理清理代码和异常,而不是我的类?还是他们无论如何都可以访问 EndInvoke 返回的任何内容,以便如果他愿意,他可以处理任何异常?

【问题讨论】:

  • 不是你的问题的真正解决方案,但你为什么不使用实际的events?
  • 要在事件上使用 begininvoke ,无论如何您都必须从中获取代表列表,并且由于出于其他原因我需要一个用于订阅/取消订阅的包装器方法,因此仅使用代表就更简单了.
  • 我在答案中添加了异常处理。

标签: c# .net multithreading delegates begininvoke


【解决方案1】:

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

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&lt;object, UpdateArgs&gt; 代表进行调整。

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

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


如果你给你的班级添加一个事件

public event EventHandler<Exception> OnError;

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

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

【讨论】:

  • 谢谢!这是第一个问题,不是第二个问题吗?
  • 是的。关于例外的部分 - 好吧,我也必须查一下。但也许这会让你自己找到答案?
  • 顺便说一句,我也见过这样的:a.BeginInvoke(a.EndInvoke, null); 但是如果有什么奇怪的地方,这将很难调试。也许你还想在回调中做一些其他的事情......
  • 我想我可以以某种方式使用订阅者作为键来创建一个公共字典/错误查找,这样至少如果确实发生错误,订阅者可以找到它们,尽管承认它会丢失很多例外的优点。
  • 好吧,您可以添加一个 OnError 事件,订阅者为“发送者”,并在 EventArgs 中添加异常。
猜你喜欢
  • 2014-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多