c# - 使用任务的意外线程中止异常。为什么?

标签 c# multithreading task

我有一个在应用程序域中运行的控制台应用程序。应用程序域由自定义 Windows 服务启动。该应用程序使用父任务来启动几个可以正常工作的子任务。当计时器寻找新工作时,在任何给定时间都可能有许多父任务和子任务在运行。

所有父任务的句柄在任务列表中:

 static List<Task> _Tasks = new List<Task>();

当管理员更改 xml 配置文件(或调用服务关闭时)时,Windows 服务能够通过将字符串 token 放入应用程序域槽中来向正在运行的应用程序发送停止信号。该应用程序在计时器上运行并检查插槽中的关闭信号,然后尝试通过等待所有任务结束来优雅地结束它正在做的事情。

任务是这样开始的:

Task parent = Task.Factory.StartNew(() =>
            {
                foreach (var invoiceList in exportBucket)
                {
                    KeyValuePair<string, List<InvoiceInfo>> invoices = new KeyValuePair<string, List<InvoiceInfo>>();
                    invoices = invoiceList;
                    string taskName = invoices.Key; //file name of input file
                    Task<bool> task = Task.Factory.StartNew<bool>(state => ExportDriver(invoices),
                        taskName, TaskCreationOptions.AttachedToParent);
                }
            });
            _Tasks.Add(parent);

自定义 GAC dll 包含一个完成工作的类。 GAC 函数中没有共享对象。 GAC 类在每个子任务中实例化:

Export export = new Export();

每个子任务在执行过程中的某个时刻调用一个方法:

foreach (var searchResultList in SearchResults)
{
      foreach (var item in searchResultList)
      {
          if (item.Documents.Count > 0)
          {
              //TODO: this is where we get thread issue if telling service to stop
              var exported = export.Execute(searchResultList);
              totalDocuments += exported.ExportedDocuments.Count();
          }
      }
 }

searchResultList 不在任务之间共享。当应用程序运行时,export.Execute 会按预期执行所有子任务。当在应用程序中检测到停止信号时,它会尝试等待所有子任务结束。我已经尝试了几种方法来等待每个父项下的子任务结束:

foreach (var task in _Tasks){task.Wait();}

while (_Tasks.Count(t => t.IsCompleted) != _Tasks.Count){}

等待代码执行时发生线程异常:

Error in Export.Execute() method: System.Threading.ThreadAbortException: Thead was being aborted at WFT.CommonExport.Export.Execute(ICollection '1 searchResults)

我不想使用取消 token ,因为我希望任务完成,而不是取消。

我不清楚为什么 GAC 类方法不愉快,因为每个任务都应该有一个唯一的方法对象句柄。

更新:

感谢您的评论。只是为了进一步说明这里发生的事情......

理论上,等待子任务的方法不应该有任何理由:

while (_Tasks.Count(t => t.IsCompleted) != _Tasks.Count){}

不过应该​​不行

Task.WaitAll()

无疑是一种更好的方法,并且有助于充实问题。进一步测试后发现,其中一个问题是当应用程序域应用程序告诉调用服务时,没有通过填充服务读取的插槽来完成任何工作:

AppDomain.CurrentDomain.SetData("Status", "Not Exporting");

该语句在代码中的时间和位置在应用程序中是错误的。作为多线程的新手,当我将 SetData 发出到“不导出”时,我花了一段时间才弄清楚任务仍在运行。因此,当服务认为可以关闭并删除应用程序域时,我认为这导致了 ThreadAbortException。我已将 SetData 语句移至更可靠的位置。

最佳答案

回答您的问题:您收到线程中止消息,因为任务是在“后台”线程上执行的。

在应用程序终止之前不等待后台线程。参见 this MSDN link进一步解释。

现在尝试帮助解决您的实际问题,我建议使用 Jim 提到的 Task.WaitAll() 方法,但是您应该更稳健地处理应用程序终止。我怀疑当您在关闭之前等待任务完成时,您不会阻止新任务入队。

我会推荐一个退出阻塞信号量和查询任务的系统在初始化时递增它,并在处置时递减。

关于c# - 使用任务的意外线程中止异常。为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7558145/

相关文章:

c# - 仅在启动线程中分配的类宽整数是否可以安全读取其值?

ruby-on-rails - Ruby 中的线程会提高并发性吗?

c# - gRPC 间歇性地具有高延迟

c# - 对称加密加密很多小块

c# - .NET Framework 和 .NET Core 之间的线程池差异、线程池饥饿

Windows 8 NetScheduleJobAdd 不支持请求

c - Linux中如何读取 "proc/pid/stat"中的开始时间数据来计算耗时?

c# - 每当我启动服务时,邮件都不会发送,但当我调试时,它会按我预期的那样发送邮件

c# - 更新 Facebook 访问 token

c# - 为许多方法编写进度条的最佳方法