应用程序在处理所有事件之前退出 - 基于事件的异步模式
Application exits before all events are handled - Event based async pattern
我正在尝试在以下位置描述的基于事件的异步示例:https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/component-that-supports-the-event-based-asynchronous-pattern。
我写的PrimeNumberCalculator
class和文章描述的完全一样。我编写了一个简单的客户端控制台应用程序来调用计算方法,如下所示:
//------------CONSOLE APP MAIN------------
static void Main(string[] args)
{
CalculatePrimeNumbers();
Console.WriteLine("Application will exit here. Press a key");
Console.ReadKey();
}
//------------CALLING CODE-----------
static void CalculatePrimeNumbers()
{
PrimeNumberCalculator primeNumberCalculator = new PrimeNumberCalculator();
//setup event handlers
primeNumberCalculator.CalculatePrimeCompleted +=
new CalculatePrimeCompletedEventHandler(primeNumberCalculator_CalculatePrimeCompleted);
primeNumberCalculator.ProgressChanged +=
new ProgressChangedEventHandler(primeNumberCalculator_ProgressChanged);
for (int i = 0; i < 100; i++)
{
Random rand = new Random();
int testNumber = rand.Next(200);
Thread.Sleep(20);
Guid taskId = Guid.NewGuid();
Console.WriteLine($"{taskId} - Starting for {testNumber}");
primeNumberCalculator.CalculatePrimeAsync(testNumber, taskId);
}
}
//------------CALCULATION COMPLETED EVENT HANDLER------------
static private void primeNumberCalculator_CalculatePrimeCompleted(
object sender, CalculatePrimeCompletedEventArgs e)
{
//await Task.Yield();
Guid taskId = (Guid)e.UserState;
Console.WriteLine($"{taskId} - Whole process completed");
}
//------------CALCULATION PROGRESSED EVENT HANDLER------------
static private void primeNumberCalculator_ProgressChanged(ProgressChangedEventArgs e)
{
Guid taskId = (Guid)e.UserState;
Console.WriteLine($"{taskId} - {e.ProgressPercentage}%");
DoSomethingImportant(taskId);
}
//------------POST PROCESSING WORK------------
static void DoSomethingImportant(Guid guid)
{
//necessary Work to be done on completion/progress;
}
当我 运行 这样做时,代码按预期工作,但在处理了大量已完成和已完成的事件后,一些事件在应用程序退出后得到处理Console.WriteLine("Application will exit here. Press a key");
3d744ab4-6d2c-4949-a1ec-ce22718daa1f - Starting for 169
d73dc2c4-a93b-49a2-8608-b2d180884295 - 69%
d73dc2c4-a93b-49a2-8608-b2d180884295 - 77%
d73dc2c4-a93b-49a2-8608-b2d180884295 - 80%
2ae4ab62-1479-4360-8e35-fdf747ed3f93 - 8%
f48a77d5-e349-4ff5-889d-c019c759a527 - Starting for 34
Application will exit here. Press a key
2ae4ab62-1479-4360-8e35-fdf747ed3f93 - 78%
f48a77d5-e349-4ff5-889d-c019c759a527 - 50%
f48a77d5-e349-4ff5-889d-c019c759a527 - 55%
f48a77d5-e349-4ff5-889d-c019c759a527 - 67%
f48a77d5-e349-4ff5-889d-c019c759a527 - 85%
f48a77d5-e349-4ff5-889d-c019c759a527 - 91%
f48a77d5-e349-4ff5-889d-c019c759a527 - Whole process completed
f48a77d5-e349-4ff5-889d-c019c759a527 - 14%
2ae4ab62-1479-4360-8e35-fdf747ed3f93 - 13%
d73dc2c4-a93b-49a2-8608-b2d180884295 - 4%
2ae4ab62-1479-4360-8e35-fdf747ed3f93 - 15%
f48a77d5-e349-4ff5-889d-c019c759a527 - 38%
f48a77d5-e349-4ff5-889d-c019c759a527 - 20%
f48a77d5-e349-4ff5-889d-c019c759a527 - 32%
在上面的输出中,在 "Application will exit here. Press a key"
行之后处理了几个事件,这将有效地跳过事件处理程序为这些任务完成的任何 post 处理。
问题
这是预期的还是我在处理程序中遗漏了什么?我想到的避免这种情况的方法之一是跟踪正在进行的计算(任务)的数量,在引发 "completed" 事件处理程序时检查它们,并坚持应用程序直到所有飞行中的计算都被勾选完成。
是否有推荐的方法来确保在应用程序退出之前处理所有事件?
基于事件的异步编程模式依靠事件来告诉您作业何时完成。您的 Main
代码中没有任何内容告诉它等待所有完成事件都已触发。
您可以使用 CountdownEvent
来做到这一点。从 100 开始计数:
static CountdownEvent cde = new CountdownEvent(100);
通过调用 Signal()
在 primeNumberCalculator_CalculatePrimeCompleted
中减一:
cde.Signal();
然后等到 Main
方法中的计数为 0:
cde.Wait();
另一种选择是使用 Task-based asynchronous pattern instead. Then you could keep a list of all the tasks and use Task.WhenAll
to asynchronously wait for them. This article can help with that too: Asynchronous programming with async and await。
我正在尝试在以下位置描述的基于事件的异步示例:https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/component-that-supports-the-event-based-asynchronous-pattern。
我写的PrimeNumberCalculator
class和文章描述的完全一样。我编写了一个简单的客户端控制台应用程序来调用计算方法,如下所示:
//------------CONSOLE APP MAIN------------
static void Main(string[] args)
{
CalculatePrimeNumbers();
Console.WriteLine("Application will exit here. Press a key");
Console.ReadKey();
}
//------------CALLING CODE-----------
static void CalculatePrimeNumbers()
{
PrimeNumberCalculator primeNumberCalculator = new PrimeNumberCalculator();
//setup event handlers
primeNumberCalculator.CalculatePrimeCompleted +=
new CalculatePrimeCompletedEventHandler(primeNumberCalculator_CalculatePrimeCompleted);
primeNumberCalculator.ProgressChanged +=
new ProgressChangedEventHandler(primeNumberCalculator_ProgressChanged);
for (int i = 0; i < 100; i++)
{
Random rand = new Random();
int testNumber = rand.Next(200);
Thread.Sleep(20);
Guid taskId = Guid.NewGuid();
Console.WriteLine($"{taskId} - Starting for {testNumber}");
primeNumberCalculator.CalculatePrimeAsync(testNumber, taskId);
}
}
//------------CALCULATION COMPLETED EVENT HANDLER------------
static private void primeNumberCalculator_CalculatePrimeCompleted(
object sender, CalculatePrimeCompletedEventArgs e)
{
//await Task.Yield();
Guid taskId = (Guid)e.UserState;
Console.WriteLine($"{taskId} - Whole process completed");
}
//------------CALCULATION PROGRESSED EVENT HANDLER------------
static private void primeNumberCalculator_ProgressChanged(ProgressChangedEventArgs e)
{
Guid taskId = (Guid)e.UserState;
Console.WriteLine($"{taskId} - {e.ProgressPercentage}%");
DoSomethingImportant(taskId);
}
//------------POST PROCESSING WORK------------
static void DoSomethingImportant(Guid guid)
{
//necessary Work to be done on completion/progress;
}
当我 运行 这样做时,代码按预期工作,但在处理了大量已完成和已完成的事件后,一些事件在应用程序退出后得到处理Console.WriteLine("Application will exit here. Press a key");
3d744ab4-6d2c-4949-a1ec-ce22718daa1f - Starting for 169
d73dc2c4-a93b-49a2-8608-b2d180884295 - 69%
d73dc2c4-a93b-49a2-8608-b2d180884295 - 77%
d73dc2c4-a93b-49a2-8608-b2d180884295 - 80%
2ae4ab62-1479-4360-8e35-fdf747ed3f93 - 8%
f48a77d5-e349-4ff5-889d-c019c759a527 - Starting for 34
Application will exit here. Press a key
2ae4ab62-1479-4360-8e35-fdf747ed3f93 - 78%
f48a77d5-e349-4ff5-889d-c019c759a527 - 50%
f48a77d5-e349-4ff5-889d-c019c759a527 - 55%
f48a77d5-e349-4ff5-889d-c019c759a527 - 67%
f48a77d5-e349-4ff5-889d-c019c759a527 - 85%
f48a77d5-e349-4ff5-889d-c019c759a527 - 91%
f48a77d5-e349-4ff5-889d-c019c759a527 - Whole process completed
f48a77d5-e349-4ff5-889d-c019c759a527 - 14%
2ae4ab62-1479-4360-8e35-fdf747ed3f93 - 13%
d73dc2c4-a93b-49a2-8608-b2d180884295 - 4%
2ae4ab62-1479-4360-8e35-fdf747ed3f93 - 15%
f48a77d5-e349-4ff5-889d-c019c759a527 - 38%
f48a77d5-e349-4ff5-889d-c019c759a527 - 20%
f48a77d5-e349-4ff5-889d-c019c759a527 - 32%
在上面的输出中,在 "Application will exit here. Press a key"
行之后处理了几个事件,这将有效地跳过事件处理程序为这些任务完成的任何 post 处理。
问题
这是预期的还是我在处理程序中遗漏了什么?我想到的避免这种情况的方法之一是跟踪正在进行的计算(任务)的数量,在引发 "completed" 事件处理程序时检查它们,并坚持应用程序直到所有飞行中的计算都被勾选完成。
是否有推荐的方法来确保在应用程序退出之前处理所有事件?
基于事件的异步编程模式依靠事件来告诉您作业何时完成。您的 Main
代码中没有任何内容告诉它等待所有完成事件都已触发。
您可以使用 CountdownEvent
来做到这一点。从 100 开始计数:
static CountdownEvent cde = new CountdownEvent(100);
通过调用 Signal()
在 primeNumberCalculator_CalculatePrimeCompleted
中减一:
cde.Signal();
然后等到 Main
方法中的计数为 0:
cde.Wait();
另一种选择是使用 Task-based asynchronous pattern instead. Then you could keep a list of all the tasks and use Task.WhenAll
to asynchronously wait for them. This article can help with that too: Asynchronous programming with async and await。