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 );
}
我目前有一个 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 );
}