使用任务发生意外的线程中止异常。为什么?

时间:2011-09-26 16:26:01

标签: c# multithreading task

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

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

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

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

任务就像这样开始:

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)

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

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

更新

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

理论上,等待儿童任务的方法应该没有任何理由:

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

不应该有效,但

Task.WaitAll()

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

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

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

1 个答案:

答案 0 :(得分:4)

回答你的问题:你正在接收线程中止,因为任务是在“后台”线程上执行的。

在应用程序终止之前,不会等待后台线程。有关详细说明,请参阅this MSDN link

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

我建议使用一个出口阻塞信号量,并且在初始化时将任务增加的系统,以及在dispose上递减。