等待子任务真正完成

Wait for child task to really compelete

我有以下代码作为子任务启动,以获取给定目录中的所有文件并根据主题执行某些操作,并为每个文件调用事件以提醒父任务:

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 的父任务调用它,分散 运行 5 个单独的任务,FileFinder 是主题之一:

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 等)中随机调用,并且不迭代所有已创建的项目。

在小文件列表中它没有显示出很大的区别

我认为父任务完成,子任务立即杀人。

有什么想法吗?

谢谢。

您的代码是我见过的代码量最大的代码之一。

这是使用 Microsoft 的 Reactive Framework (NuGet "Rx-Main") 编写的相同代码。

var query =
    from dir in directoryList.ToObservable()
    from file in
            Directory.EnumerateFiles(dir, tbFileFormat.Text, SearchOption.AllDirectories)
    select file;

var subscription = query.Subscribe(file =>
{
    ParseFinish(file);
});

就是这样。这一切都是使用后台线程处理的。它摆脱了所有这些 类,只做你需要的工作。

如果你想中途取消,就这样做:

subscription.Dispose();

如果您想知道它何时完成,只需执行以下操作:

var subscription = query.Subscribe(file =>
{
    ParseFinish(file);
}, () =>
{
    /* Handle the query is finished here */
});