我正在处理EventManager
,负责注册一些Subscribers
并在发生事件时通知他们。
我想模拟一个循环场景,找到一种方法来阻止它。这是我写的测试:
[Test]
public void LoopTest()
{
var eventManager = new EventManager();
eventManager.IntroduceEvent("A",typeof(EventArgs));
eventManager.IntroduceEvent("B", typeof(EventArgs));
eventManager.Subscribe<EventArgs>("A", (sender, args) =>
{
Console.WriteLine("Raise B");
eventManager.RaiseAsync("B", sender, args);
});
eventManager.Subscribe<EventArgs>("B", (sender, args) =>
{
Console.WriteLine("Raise A");
eventManager.RaiseAsync("A", sender, args);
});
eventManager.RaiseAsync<EventArgs>("A",null,null).Wait();
}
这是async
方法:
public Task RaiseAsync<T>(string eventName, object sender, T eventArgs)
{
EnsureEventIsIntroduced(eventName, typeof (T));
var tasks = new List<Task>();
_subscribers[eventName].Values.ToList()
.ForEach(
subscriber =>
tasks.Add(Task.Factory.StartNew(() => ((Action<object, T>) subscriber)(sender, eventArgs))));
return Task.WhenAll(tasks);
}
当我使用Resharper测试运行器运行此测试时,我在输出和测试通道中看到以下结果。
Raise B
Raise A
Raise B
Raise A
Raise B
虽然,我希望这个测试应该产生一个无限循环。你能解释一下发生了什么吗? (但是,此测试的sync
版本会产生无限循环。)
答案 0 :(得分:2)
有三个原因你没有看到无限循环。
首先,您的测试不等待事件完成。您应该将测试更改为:
[Test]
public async Task LoopTest()
{
...
await eventManager.RaiseAsync<EventArgs>("A", null, null);
}
其次,当您添加再次引发事件的订阅者时, 不等待事件完成。
第三,在RaiseAsync
中,您只等待开始新任务完成的任务。您并没有等待订阅者自己完成。
顺便说一下,我强烈建议您在foreach
方法中使用Select
循环 - 或仅RaiseAsync
- 。它会更清楚:
var tasks = _subscribers[eventName]
.Values
.Cast<Action<object, T>>()
.Select(subscriber => Task.Run(() => subscriber(sender, eventArgs)))
.ToList();
虽然不太清楚你实际想要发生什么,这使得很难提供正确的工作代码。如果您想要异步事件处理程序,它们应该是Func<object, T, Task>
而不是Action<object, T>
,您使用异步lambdas订阅它们。
答案 1 :(得分:2)
调用eventManager.RaiseAsync<EventArgs>("A",null,null);
之后,任何后续的RaiseAsync调用都将在eventhandler中发生 - 并触发另一个异步任务。
然而,您的FIRST任务现在可以完成,因此您的调用将返回并且方法结束。发生这种情况时,会触发另外4个事件。
如果您在第一次通话后添加睡眠声明,您应该会看到更多后续异步事件处理过的方式:
[Test]
public void LoopTest()
{
var eventManager = new EventManager();
eventManager.IntroduceEvent("A",typeof(EventArgs));
eventManager.IntroduceEvent("B", typeof(EventArgs));
eventManager.Subscribe<EventArgs>("A", (sender, args) =>
{
Console.WriteLine("Raise B");
eventManager.RaiseAsync("B", sender, args);
});
eventManager.Subscribe<EventArgs>("B", (sender, args) =>
{
Console.WriteLine("Raise A");
eventManager.RaiseAsync("A", sender, args);
});
eventManager.RaiseAsync<EventArgs>("A",null,null);
//Wait a little bit to get more async events processed
System.Threading.Thread.Sleep(1000);
}
现在,主线程将被冻结1000ms,允许独立的异步子任务提升并处理更多事件。
在现场环境中,这将是一个无限循环。在这里,我假设测试环境在TestMethod结束后杀死所有其他异步任务很简单。