【发布时间】:2015-09-21 12:56:11
【问题描述】:
我有以下代码作为子任务开始,以获取给定目录中的所有文件并在主题上执行某些操作并为每个文件调用事件以提醒父任务:
internal class FileFinder
{
private readonly string _fileFormat;
public delegate void FileFoundDelegate(string filePath);
public event FileFoundDelegate OnFileFound;
public FileFinder(string fileFormat)
{
_fileFormat = fileFormat;
}
public bool Start(CancellationToken cancellationToken, string directory)
{
try
{
if (OnFileFound == null)
return false;
var foundedFiles = new ThreadLocal<IEnumerable<string>>();
try
{
foundedFiles.Value = Directory.EnumerateFiles(directory, _fileFormat, SearchOption.AllDirectories)
.AsParallel();
}
catch (Exception ex)
{
Debug.WriteLine("Parallel : " + ex.Message);
}
foreach (var file in foundedFiles.Value)
{
if (cancellationToken.IsCancellationRequested)
return true;
// Call file found event with normalized file name
OnFileFound?.Invoke(file);
}
return true;
}
catch (Exception ex)
{
Common.DebugLog(System.Reflection.MethodBase.GetCurrentMethod().Name,
ex.InnerException?.Message ?? ex.Message);
return false;
}
}
}
并使用名为 Scatter 的父任务调用它,FileFinder 是主题之一的分散运行 5 个单独任务:
internal class Scatter
{
private readonly CancellationToken _cancellationToken;
private readonly string _directory;
private readonly string _fileFormat;
private FileFinder _emailFinder;
public Scatter(CancellationToken cancellationToken, string directory, string fileFormat)
{
_cancellationToken = cancellationToken;
_directory = directory;
_fileFormat = fileFormat;
}
public Task Start()
{
try
{
return Task.Factory.StartNew(StartProc,
TaskCreationOptions.AttachedToParent | TaskCreationOptions.LongRunning);
}
catch (Exception)
{
return null;
}
}
private void StartProc()
{
try
{
// Find pdf files
_emailFinder = new FileFinder(_fileFormat);
_emailFinder.OnFileFound += FileFound;
Task.Factory.StartNew(() => _emailFinder.Start(_cancellationToken, _directory),
TaskCreationOptions.AttachedToParent | TaskCreationOptions.LongRunning);
}
catch (Exception ex)
{
Common.DebugLog(System.Reflection.MethodBase.GetCurrentMethod().Name, ex.InnerException.Message);
}
}
private void FileFound(string filePath)
{
Debug.WriteLine("File Found");
}
}
最后一个主任务为每个目录运行单独的分散:
internal class Master
{
private readonly CancellationToken _cancellationToken;
internal delegate void ParseFinish();
public event ParseFinish OnParseFinish;
public Master(CancellationToken cancellationToken)
{
_cancellationToken = cancellationToken;
}
public bool Start(List<string> targetDirectories, string fileFormat)
{
try
{
Task.Factory.StartNew(() => StartProc(targetDirectories, fileFormat), _cancellationToken);
return true;
}
catch (Exception ex)
{
Common.DebugLog(System.Reflection.MethodBase.GetCurrentMethod().Name,
ex.InnerException?.Message ?? ex.Message);
return false;
}
}
private bool StartProc(List<string> directories, string fileFormat)
{
try
{
List<Task> targetScatterList = new List<Task>();
foreach (string dir in directories)
{
var scatter = new Scatter(_cancellationToken,dir, fileFormat);
targetScatterList.Add(scatter.Start());
}
// Wait for finish all tasks & call parse finish event
Task.WaitAll(targetScatterList.ToArray());
OnParseFinish?.Invoke();
return true;
}
catch (Exception ex)
{
Common.DebugLog(System.Reflection.MethodBase.GetCurrentMethod().Name,
ex.InnerException?.Message ?? ex.Message);
return false;
}
}
}
我有主任务等待所有目录的任务完成并且不涉及应用程序主线程。
像这样从主线程调用主任务:
List<string> directoryList = ListBox1.Items.Cast<string>().ToList();
// Create cancelation token
_cancellationTokenSource = new CancellationTokenSource();
_cancellationToken = _cancellationTokenSource.Token;
// Start master task that populate new task for each target
var masterTask= new Master(_cancellationToken);
masterTask.OnParseFinish += ParseFinish;
masterTask.Start(directoryList, tbFileFormat.Text);
我在示例图书目录中有 287,198 个 PDF 文件,FileFound 事件在不同的项目运行(287170、287182、287146 等)中称为随机时间,并且不会迭代所有已创建的项目。
在小文件列表中它并没有显示出很大的差异
我认为父任务完成,子任务立即杀死。
有什么想法吗?
谢谢。
【问题讨论】:
-
只是为了它,你能做一个
Console.WriteLine(foundedFiles.Value.Length)以确保枚举本身是完整的吗? -
您在
StartProc方法中启动了一项任务,而不是等待它。您提到您的主线程正在等待父任务,但您的父任务没有等待子任务。 -
@EdT 不正确 - 如果子任务以 AttachedToParent 标志启动,则等待父任务将仅在所有附加子任务完成后返回。
-
无法重现所描述的行为。对我来说,提供的代码会遍历所有找到的 pdf 文件。
-
您在异常处理中是从哪里学到的?即使只是做
catch (Exception ex)已经够糟糕了,但是到处重复它是糟糕的编码。你到处隐藏错误。
标签: c# multithreading task-parallel-library task