我有一个在应用程序域中运行的控制台应用程序。应用程序域由自定义 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();}
and
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 类方法不高兴,因为每个任务都应该有一个唯一的方法对象句柄。
UPDATE:
感谢您的评论。只是为了进一步澄清这里发生的事情......
理论上,等待子任务的方法不应该有任何理由:
while (_Tasks.Count(t => t.IsCompleted) != _Tasks.Count){}
不应该工作,但
Task.WaitAll()
这当然是一个更好的方法,并有助于充实问题。经过进一步测试后发现,问题之一是当应用程序域应用程序告诉调用服务时,没有通过填充服务读取的槽来完成任何工作:
AppDomain.CurrentDomain.SetData("Status", "Not Exporting");
应用程序中该语句在代码中的时间和位置是错误的。作为多线程新手,我花了一段时间才发现当我发出命令时任务仍在运行SetData
至“不导出”。因此,当该服务认为可以关闭并拆除应用程序域时,我相信这导致了ThreadAbortException
。从那时起我就搬走了SetData
声明到更可靠的位置。