Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 对.NET中的服务使用Task.WaitAll()时出现问题_C#_.net_Task Parallel Library_Task - Fatal编程技术网

C# 对.NET中的服务使用Task.WaitAll()时出现问题

C# 对.NET中的服务使用Task.WaitAll()时出现问题,c#,.net,task-parallel-library,task,C#,.net,Task Parallel Library,Task,我有一个具有以下OnStart()方法的服务: 和OnStop()方法: StartManager()方法的实现如下所示: public void StartManager() { foreach (var reportGeneratorThread in this.ReportGenerators) { reportGeneratorThread.Start(); Thread.Sleep(1000); }

我有一个具有以下OnStart()方法的服务:

和OnStop()方法:

StartManager()方法的实现如下所示:

public void StartManager()
{
    foreach (var reportGeneratorThread in this.ReportGenerators)
    {                
        reportGeneratorThread.Start();
        Thread.Sleep(1000);
    }

    try
    { 
        Task.WaitAll(this.Tasks.ToArray());
    }
    catch (AggregateException e)
    {
        foreach (var v in e.InnerExceptions)
            {
                var taskException = v as TaskCanceledException;
                if (v != taskException)
                {
                    foreach (var reportGenerator in this.ReportGenerators)
                    {
                        if (reportGenerator.Task.IsFaulted)
                        {
                            this.logger.LogEvent(this.Id.ToString(), string.Format(CultureInfo.InvariantCulture, "System"), string.Format(CultureInfo.InvariantCulture, "Unhandled exception from Task " + reportGenerator.Task.Id));                             
                            ReportGeneratorThread faultedReportGeneratorThread = this.GetThreadById(reportGenerator.Task.Id);
                            var index = this.ReportGenerators.FindIndex(t => t.Equals(faultedReportGeneratorThread));
                            this.DisposeFaultedThread(faultedReportGeneratorThread, index);                           
                            this.ReportGenerators[index].Start();    
                            this.logger.LogDebug(string.Format(CultureInfo.InvariantCulture, "Faulted Task, and instance of ReportGeneratorThread is recreated and corresponding task is started"));
                            break;
                        }
                    }
                }
                else if (taskException != null)
                {
                    this.logger.LogEvent(this.Id.ToString(), string.Format(CultureInfo.InvariantCulture, "System"), string.Format(CultureInfo.InvariantCulture, "Task " + taskException.Task.Id + " has thrown a Task Canceled Exception"));
                }
            }
    }
}
这个问题发生在我的StartManager()方法中,因为reportGeneratorRead.Start()方法正在启动一个任务,该任务在while循环中连续生成一个报告,只有在抛出一个取消令牌并且我在我的service OnStop()方法中抛出该令牌时,才能中止该任务。 因此,当我测试我的服务时,程序无法到达Task.WaitAll()以外的位置,这会阻止我完成OnStart()方法,并且我收到以下错误:

 Error 1053: the service did not respond to the start or control request in a timely fashion    
我仍然需要管理我的任务,所以我实际上需要task.WaitAll()方法,但我也需要解决这个问题。在这种情况下,如何完成OnStart()方法?。不改变代码结构的最佳方法是什么

添加我的代码的更多部分:

这是我的任务调用的方法:

 private void DoWork()
    {
        while (this.Running)
        {
            this.GenerateReport();
            Thread.Sleep(Settings.Default.DefaultSleepDelay);
        }

        this.log.LogDebug(string.Format(CultureInfo.InvariantCulture, "Worker thread stopping."));
    }
和GenerateReport()方法:如果服务请求取消,我调用Stop()方法。此方法引发TaskCancelledException

 public void GenerateReport()
    {
        if (this.cancellationToken.IsCancellationRequested)
        {
            this.Stop();
        }

        var didwork = false;
        try
        {
            didwork = this.reportGenerator.GenerateReport(this.getPermission, this.TaskId);
        }
        catch (Exception e)
        {
            this.log.LogError(ReportGenerator.CorrelationIdForPickingReport, string.Format(CultureInfo.InvariantCulture, "System"), string.Format(CultureInfo.InvariantCulture, "Error during report generation."), 0, e);
        }
        finally
        {
            if (!didwork)
            {
               Thread.Sleep(Settings.Default.ReportGenerationInterval);
            }
        }
    }
如果不清楚地说明您的问题,就很难准确地理解代码在做什么。我马上就看到了至少一些奇怪的事情:

  • 您的方法声称要重新启动报表生成器线程。但就我所知,它只能重启一个。如果您以后还有任何错误,就不会有代码等待检测和处理
  • 在所有任务完成之前,使用
    WaitAll()
    将防止代码检测到哪怕一个故障。因此,它们要么都需要在重新启动之前出现故障,要么在实际停止服务并取消任务之前,您不会检测到故障
  • 唯一的时间
    v!=如果
    taskException
    null
    ,则taskException
    将为true。所以在我看来,以后检查它是否为非null是毫无意义的
  • 我不认为在开始每项任务之间睡一秒钟有什么意义
  • 所以我不确定是否有可能知道修复所有这些代码的最佳方法是什么。也就是说,眼前的问题似乎很清楚:您的
    OnStart()
    方法在设计上需要及时返回,但您当前的实现没有做到这一点。通过将
    StartManager()
    方法设置为
    async
    方法,并使用
    wait
    将控制权返回给调用方,直到发生有趣的事情,这个基本问题似乎是可以解决的。可能看起来像这样:

    public async Task StartManager()
    {
        foreach (var reportGeneratorThread in this.ReportGenerators)
        {                
            reportGeneratorThread.Start();
            Thread.Sleep(1000);
        }
    
        try
        { 
            await Task.WhenAll(this.Tasks.ToArray());
        }
        catch (AggregateException e)
        {
            foreach (var v in e.InnerExceptions)
                {
                    var taskException = v as TaskCanceledException;
                    if (v != taskException)
                    {
                        foreach (var reportGenerator in this.ReportGenerators)
                        {
                            if (reportGenerator.Task.IsFaulted)
                            {
                                this.logger.LogEvent(this.Id.ToString(), string.Format(CultureInfo.InvariantCulture, "System"), string.Format(CultureInfo.InvariantCulture, "Unhandled exception from Task " + reportGenerator.Task.Id));                             
                                ReportGeneratorThread faultedReportGeneratorThread = this.GetThreadById(reportGenerator.Task.Id);
                                var index = this.ReportGenerators.FindIndex(t => t.Equals(faultedReportGeneratorThread));
                                this.DisposeFaultedThread(faultedReportGeneratorThread, index);                           
                                this.ReportGenerators[index].Start();    
                                this.logger.LogDebug(string.Format(CultureInfo.InvariantCulture, "Faulted Task, and instance of ReportGeneratorThread is recreated and corresponding task is started"));
                                break;
                            }
                        }
                    }
                    else if (taskException != null)
                    {
                        this.logger.LogEvent(this.Id.ToString(), string.Format(CultureInfo.InvariantCulture, "System"), string.Format(CultureInfo.InvariantCulture, "Task " + taskException.Task.Id + " has thrown a Task Canceled Exception"));
                    }
                }
        }
    }
    
    protected override void OnStart(string[] args)
    {
      // Save the returned Task in a local, just as a hack
      // to suppress the compiler warning about not awaiting the call.
      // Alternatively, store the Task object somewhere and actually
      // do something useful with it.
      var _ = this.manager.StartManager();
      this.log.LogEvent(this.id.ToString(), string.Format(CultureInfo.InvariantCulture, "System"), string.Format(CultureInfo.InvariantCulture, "Service started"));
    }
    
    public async Task StartManager()
    {
        foreach (var reportGeneratorThread in this.ReportGenerators)
        {                
            reportGeneratorThread.Start();
        }
    
        while (true)
        {
            try
            { 
                Task task = await Task.WhenAny(this.Tasks.ToArray());
    
                if (task.IsFaulted)
                {
                    // Unpack the exception. Alternatively, you could just retrieve the
                    // AggregateException directly from task.Exception and process it
                    // exactly as in the original code (i.e. enumerate the
                    // AggregateException.InnerExceptions collection). Note that in
                    // that case, you will see only a single exception in the
                    // InnerExceptions collection. To detect exceptions in additional
                    // tasks, you would need to await them as well. Fortunately,
                    // this will happen each time you loop back and call Task.WhenAny()
                    // again, since all the tasks are in the Tasks collection being
                    // passed to WhenAny().
    
                    await task;
                }
            }
            catch (Exception v)
            {
                var taskException = v as TaskCanceledException;
                if (v != taskException)
                {
                    foreach (var reportGenerator in this.ReportGenerators)
                    {
                        if (reportGenerator.Task.IsFaulted)
                        {
                            this.logger.LogEvent(this.Id.ToString(), string.Format(CultureInfo.InvariantCulture, "System"), string.Format(CultureInfo.InvariantCulture, "Unhandled exception from Task " + reportGenerator.Task.Id));                             
                            ReportGeneratorThread faultedReportGeneratorThread = this.GetThreadById(reportGenerator.Task.Id);
                            var index = this.ReportGenerators.FindIndex(t => t.Equals(faultedReportGeneratorThread));
                            this.DisposeFaultedThread(faultedReportGeneratorThread, index);                           
                            this.ReportGenerators[index].Start();    
                            this.logger.LogDebug(string.Format(CultureInfo.InvariantCulture, "Faulted Task, and instance of ReportGeneratorThread is recreated and corresponding task is started"));
                            break;
                        }
                    }
                }
                else
                {
                    this.logger.LogEvent(this.Id.ToString(), string.Format(CultureInfo.InvariantCulture, "System"), string.Format(CultureInfo.InvariantCulture, "Task " + taskException.Task.Id + " has thrown a Task Canceled Exception"));
    
                    // Cancelling tasks...time to exit
                    return;
                }
            }
        }    
    }
    
    然后您可以从
    OnStart()
    调用它,如下所示:

    public async Task StartManager()
    {
        foreach (var reportGeneratorThread in this.ReportGenerators)
        {                
            reportGeneratorThread.Start();
            Thread.Sleep(1000);
        }
    
        try
        { 
            await Task.WhenAll(this.Tasks.ToArray());
        }
        catch (AggregateException e)
        {
            foreach (var v in e.InnerExceptions)
                {
                    var taskException = v as TaskCanceledException;
                    if (v != taskException)
                    {
                        foreach (var reportGenerator in this.ReportGenerators)
                        {
                            if (reportGenerator.Task.IsFaulted)
                            {
                                this.logger.LogEvent(this.Id.ToString(), string.Format(CultureInfo.InvariantCulture, "System"), string.Format(CultureInfo.InvariantCulture, "Unhandled exception from Task " + reportGenerator.Task.Id));                             
                                ReportGeneratorThread faultedReportGeneratorThread = this.GetThreadById(reportGenerator.Task.Id);
                                var index = this.ReportGenerators.FindIndex(t => t.Equals(faultedReportGeneratorThread));
                                this.DisposeFaultedThread(faultedReportGeneratorThread, index);                           
                                this.ReportGenerators[index].Start();    
                                this.logger.LogDebug(string.Format(CultureInfo.InvariantCulture, "Faulted Task, and instance of ReportGeneratorThread is recreated and corresponding task is started"));
                                break;
                            }
                        }
                    }
                    else if (taskException != null)
                    {
                        this.logger.LogEvent(this.Id.ToString(), string.Format(CultureInfo.InvariantCulture, "System"), string.Format(CultureInfo.InvariantCulture, "Task " + taskException.Task.Id + " has thrown a Task Canceled Exception"));
                    }
                }
        }
    }
    
    protected override void OnStart(string[] args)
    {
      // Save the returned Task in a local, just as a hack
      // to suppress the compiler warning about not awaiting the call.
      // Alternatively, store the Task object somewhere and actually
      // do something useful with it.
      var _ = this.manager.StartManager();
      this.log.LogEvent(this.id.ToString(), string.Format(CultureInfo.InvariantCulture, "System"), string.Format(CultureInfo.InvariantCulture, "Service started"));
    }
    
    public async Task StartManager()
    {
        foreach (var reportGeneratorThread in this.ReportGenerators)
        {                
            reportGeneratorThread.Start();
        }
    
        while (true)
        {
            try
            { 
                Task task = await Task.WhenAny(this.Tasks.ToArray());
    
                if (task.IsFaulted)
                {
                    // Unpack the exception. Alternatively, you could just retrieve the
                    // AggregateException directly from task.Exception and process it
                    // exactly as in the original code (i.e. enumerate the
                    // AggregateException.InnerExceptions collection). Note that in
                    // that case, you will see only a single exception in the
                    // InnerExceptions collection. To detect exceptions in additional
                    // tasks, you would need to await them as well. Fortunately,
                    // this will happen each time you loop back and call Task.WhenAny()
                    // again, since all the tasks are in the Tasks collection being
                    // passed to WhenAny().
    
                    await task;
                }
            }
            catch (Exception v)
            {
                var taskException = v as TaskCanceledException;
                if (v != taskException)
                {
                    foreach (var reportGenerator in this.ReportGenerators)
                    {
                        if (reportGenerator.Task.IsFaulted)
                        {
                            this.logger.LogEvent(this.Id.ToString(), string.Format(CultureInfo.InvariantCulture, "System"), string.Format(CultureInfo.InvariantCulture, "Unhandled exception from Task " + reportGenerator.Task.Id));                             
                            ReportGeneratorThread faultedReportGeneratorThread = this.GetThreadById(reportGenerator.Task.Id);
                            var index = this.ReportGenerators.FindIndex(t => t.Equals(faultedReportGeneratorThread));
                            this.DisposeFaultedThread(faultedReportGeneratorThread, index);                           
                            this.ReportGenerators[index].Start();    
                            this.logger.LogDebug(string.Format(CultureInfo.InvariantCulture, "Faulted Task, and instance of ReportGeneratorThread is recreated and corresponding task is started"));
                            break;
                        }
                    }
                }
                else
                {
                    this.logger.LogEvent(this.Id.ToString(), string.Format(CultureInfo.InvariantCulture, "System"), string.Format(CultureInfo.InvariantCulture, "Task " + taskException.Task.Id + " has thrown a Task Canceled Exception"));
    
                    // Cancelling tasks...time to exit
                    return;
                }
            }
        }    
    }
    
    请注意,上面提出的更改并没有解决我提到的任何奇怪之处。在我看来,该方法更有用的实现可能如下所示:

    public async Task StartManager()
    {
        foreach (var reportGeneratorThread in this.ReportGenerators)
        {                
            reportGeneratorThread.Start();
            Thread.Sleep(1000);
        }
    
        try
        { 
            await Task.WhenAll(this.Tasks.ToArray());
        }
        catch (AggregateException e)
        {
            foreach (var v in e.InnerExceptions)
                {
                    var taskException = v as TaskCanceledException;
                    if (v != taskException)
                    {
                        foreach (var reportGenerator in this.ReportGenerators)
                        {
                            if (reportGenerator.Task.IsFaulted)
                            {
                                this.logger.LogEvent(this.Id.ToString(), string.Format(CultureInfo.InvariantCulture, "System"), string.Format(CultureInfo.InvariantCulture, "Unhandled exception from Task " + reportGenerator.Task.Id));                             
                                ReportGeneratorThread faultedReportGeneratorThread = this.GetThreadById(reportGenerator.Task.Id);
                                var index = this.ReportGenerators.FindIndex(t => t.Equals(faultedReportGeneratorThread));
                                this.DisposeFaultedThread(faultedReportGeneratorThread, index);                           
                                this.ReportGenerators[index].Start();    
                                this.logger.LogDebug(string.Format(CultureInfo.InvariantCulture, "Faulted Task, and instance of ReportGeneratorThread is recreated and corresponding task is started"));
                                break;
                            }
                        }
                    }
                    else if (taskException != null)
                    {
                        this.logger.LogEvent(this.Id.ToString(), string.Format(CultureInfo.InvariantCulture, "System"), string.Format(CultureInfo.InvariantCulture, "Task " + taskException.Task.Id + " has thrown a Task Canceled Exception"));
                    }
                }
        }
    }
    
    protected override void OnStart(string[] args)
    {
      // Save the returned Task in a local, just as a hack
      // to suppress the compiler warning about not awaiting the call.
      // Alternatively, store the Task object somewhere and actually
      // do something useful with it.
      var _ = this.manager.StartManager();
      this.log.LogEvent(this.id.ToString(), string.Format(CultureInfo.InvariantCulture, "System"), string.Format(CultureInfo.InvariantCulture, "Service started"));
    }
    
    public async Task StartManager()
    {
        foreach (var reportGeneratorThread in this.ReportGenerators)
        {                
            reportGeneratorThread.Start();
        }
    
        while (true)
        {
            try
            { 
                Task task = await Task.WhenAny(this.Tasks.ToArray());
    
                if (task.IsFaulted)
                {
                    // Unpack the exception. Alternatively, you could just retrieve the
                    // AggregateException directly from task.Exception and process it
                    // exactly as in the original code (i.e. enumerate the
                    // AggregateException.InnerExceptions collection). Note that in
                    // that case, you will see only a single exception in the
                    // InnerExceptions collection. To detect exceptions in additional
                    // tasks, you would need to await them as well. Fortunately,
                    // this will happen each time you loop back and call Task.WhenAny()
                    // again, since all the tasks are in the Tasks collection being
                    // passed to WhenAny().
    
                    await task;
                }
            }
            catch (Exception v)
            {
                var taskException = v as TaskCanceledException;
                if (v != taskException)
                {
                    foreach (var reportGenerator in this.ReportGenerators)
                    {
                        if (reportGenerator.Task.IsFaulted)
                        {
                            this.logger.LogEvent(this.Id.ToString(), string.Format(CultureInfo.InvariantCulture, "System"), string.Format(CultureInfo.InvariantCulture, "Unhandled exception from Task " + reportGenerator.Task.Id));                             
                            ReportGeneratorThread faultedReportGeneratorThread = this.GetThreadById(reportGenerator.Task.Id);
                            var index = this.ReportGenerators.FindIndex(t => t.Equals(faultedReportGeneratorThread));
                            this.DisposeFaultedThread(faultedReportGeneratorThread, index);                           
                            this.ReportGenerators[index].Start();    
                            this.logger.LogDebug(string.Format(CultureInfo.InvariantCulture, "Faulted Task, and instance of ReportGeneratorThread is recreated and corresponding task is started"));
                            break;
                        }
                    }
                }
                else
                {
                    this.logger.LogEvent(this.Id.ToString(), string.Format(CultureInfo.InvariantCulture, "System"), string.Format(CultureInfo.InvariantCulture, "Task " + taskException.Task.Id + " has thrown a Task Canceled Exception"));
    
                    // Cancelling tasks...time to exit
                    return;
                }
            }
        }    
    }
    
    上述操作将循环执行,出现故障时立即重新启动任务,但如果取消任务,则会完全退出


    注意:缺少一个好的代码示例,上面是浏览器代码:完全未编译,未经测试。我不记得如何从
    WhenAll()
    WhenAny()
    传播异常的细节。我认为我的示例是对的,但完全可能您需要调整细节以使其正常工作。我希望至少能以一种有用的方式表达基本思想。

    您不需要
    任务。WaitAll
    。请详细解释您为什么认为需要
    WaitAll()
    ,我没有看到你做任何事情,除了处理可以在运行的方法
    reportGeneratorThread
    中完成的错误。你在哪里检查取消?您是否假设调用
    .Cancel()
    会在任务中引发异常?为了避免混淆,我以前没有在AggregateException中包含我的代码,但现在已经更新了它。我需要使用Task.WaitAll()方法来重新启动出现故障的线程。感谢您的回复。我能问你为什么在你的提案中不使用AggregateException吗?@OsmanEsen:仅在第二个例子中。正如我所指出的,虽然我无法确定如何从
    WhenAny()
    报告异常,但我记得
    WhenAny()
    只是返回一个未打包的异常,而不是像
    WhenAll()
    那样返回一个
    aggregateeexception
    。我今天还没有费心去测试这个,但似乎证实了我的假设。我已经实现了这个建议,但我甚至无法使用Task捕捉到一般异常。WhenAny()@OsmanEsen:对不起,我的错误。我忘记了
    WhenAny()
    返回已完成的任务。然后,您需要等待
    任务
    对象(该对象随后将引发异常),或者需要明确检查对象的状态,并从
    任务.exception
    属性检索任何异常。我将更新代码示例以进行说明。非常感谢。此解决方案是否会导致死锁(异步等待)?。