【问题标题】:Application exits before all events are handled - Event based async pattern在处理所有事件之前应用程序退出 - 基于事件的异步模式
【发布时间】:2019-11-24 22:35:00
【问题描述】:

我正在尝试在以下位置描述的基于事件的异步示例:https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/component-that-supports-the-event-based-asynchronous-pattern

我写的PrimeNumberCalculator类和文章中描述的完全一样。我编写了一个简单的客户端控制台应用程序来调用Calculate方法,如下所示:


        //------------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" 行之后处理了几个事件,这将有效地跳过事件处理程序为这些任务完成的任何后期处理。

问题

  1. 这是预期的还是我在处理程序中遗漏了什么?我想到的避免这种情况的方法之一是跟踪正在进行的计算(任务)的数量,在引发“完成”事件处理程序时检查它们,并保持应用程序直到所有飞行中的计算已经完成。

  2. 是否有推荐的方法来确保在应用程序退出之前处理所有事件?

【问题讨论】:

标签: c# events event-handling


【解决方案1】:

基于事件的异步编程模式依赖于事件来告诉您作业何时完成。您的 Main 代码中没有任何内容告诉它等待所有完成事件触发。

您可以使用CountdownEvent 来执行此操作。从 100 开始计数:

static CountdownEvent cde = new CountdownEvent(100);

通过调用Signal()primeNumberCalculator_CalculatePrimeCompleted中减一:

cde.Signal();

然后等到Main 方法中的计数为 0:

cde.Wait();

另一种选择是改用Task-based asynchronous pattern。然后您可以保留所有任务的列表并使用Task.WhenAll 异步等待它们。这篇文章也可以提供帮助:Asynchronous programming with async and await

【讨论】:

    猜你喜欢
    • 2016-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多