【问题标题】:Event based Task framework for C# completing before process complete用于 C# 在流程完成之前完成的基于事件的任务框架
【发布时间】:2022-02-02 09:22:30
【问题描述】:

我正在尝试创建一个系统来处理作为单独任务进入的事件。这一切都在一个 Windows 服务应用程序中运行,直到它停止,当停止时,等到所有任务完成后再停止。

我有以下简化的代码:

IList<Task> _tasks = new List<Task>();//this is a private class variable.

private void Event_Fired(object sender, eventArgs e)
{
    StartTask();
}

private void StartTask(){
    Task task = Task.Factory.StartNew(async () => await process.ProcessEvent(true, StoppingToken));
   _tasks.Add(task);
}

private void MonitorTasks(){
    for (int i = _tasks.Count - 1; i >= 0; i--)
    {
        Task aTask = _laneTasks[i];
        if (aTask.IsCompleted)
        {
            _tasks.Remove(aTask);
            Log.Information($"Task {aTask.Id} completed.");
        }
    }
}

public async Task ProcessEvent(){
    //do some async processing
    await Task.Delay(5000); //if I add a delay before returning the Task, it is completed prior to this delay.
}

我遇到的问题是任务在 ProcessEvent 完成之前完成。在允许进程停止之前,我需要确保任务已完成。

该过程还必须允许新事件随时进入并产生新任务。

我觉得我在这里遗漏了一些基本的东西。任何帮助将不胜感激。

【问题讨论】:

  • 旁注:开始任务只是为了开始一个任务是超级混乱......考虑不这样做。
  • 阿列克谢的意思是你可以这样做:Task task = process.ProcessEvent();(注意没有await
  • 另外,在ProcessEvent 之前完成了哪个任务?你是如何确定的?
  • 这有点难以用语言表达...抓住你最喜欢的橡皮鸭并在Task.Factory.StartNew(async () =&gt; await ... 代码行中解释它(首先阅读文档)。特别注意内部任务会发生什么......并且可能阅读“C#中热任务与冷任务的含义”。
  • “启动 ProcessEvent 的任务在返回之前完成” - 你怎么知道的?

标签: c# events async-await task


【解决方案1】:

Task.Factory.StartNew(...) 创建一个新的Task 来执行提供的委托。返回的Task 的具体类型取决于委托的返回类型。

让我们看看你传入的委托:

async () => await process.ProcessEvent(true, StoppingToken)

好的,那是Func&lt;Task&gt;。调用该函数将创建一个新的Task 并返回它。所以你的Task&lt;Task&gt; 立即完成,Result 值设置为内部Task 对象。

为了让事情更复杂一点,ProcessEvent() 本身会创建一个 Task,这就是您最终想要等待的内容。所以你正在创建一个Task 来创建一个Task 来等待Task。当然,最外层的Task 会在服务员Task 创建后立即结束,因为它的工作现在完成

由于您的代码已经在 Task 对象的集合中进行手动等待,因此您不需要 Task 其全部目的是等待最里面的 Task 完成。由于您已经从ProcessEvent() 返回了Task,因此您不需要Task 来创建Task

改为这样做:

_tasks.Add(ProcessEvent());

【讨论】:

  • 啊,是的,我知道我现在做错了什么。如果我只是去 Task task = ProcessEvent() 它可以工作。谢谢。这是我弄错的基本部分。 ProcessEvent 已经返回一个任务。然后我需要自己调用 Start() 。 Task.Run 似乎也可以工作。
  • 但是,请记住 ProcessEvent() 在遇到第一个 await 之前不会返回。您可能仍想使用await Task.Yield()Task.Run
  • @JeremyLakeman 谢谢。无论如何它并没有做太多,所以很快就会返回。
【解决方案2】:

这可能是你的问题:

Task task = Task.Factory.StartNew(async () => await process.ProcessEvent(true, StoppingToken));

这会启动一个新任务,该任务会立即完成。请尝试运行任务。

Task task = Task.Run(() => process.ProcessEvent(true, StoppingToken));

【讨论】:

  • 行不通。调用 ProcessEvent 创建一个 Task。他所等待的只是构建步骤的完成,而不是 ProcessEvent 代码的完整运行。
  • @Corey 实际上Task.Run 方法可以理解异步委托,并自动解开嵌套的Task&lt;Task&gt;s,这与Task.Factory.StartNew 方法不同。你可以阅读更多关于这个here的信息。
  • 所以Task.Run() 做了很多我们可以通过捕获调用ProcessEent 创建的Task 来避免的工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-16
  • 2018-05-07
  • 1970-01-01
  • 2017-08-07
  • 1970-01-01
相关资源
最近更新 更多