轮询新文件,避免在处理文件时重复

时间:2017-11-03 09:48:13

标签: c# concurrency

我想从Windows服务中查询远程目录中的新文件(pdf为几MB)。

每个文件都必须由密集的CPU作业处理(pdf文件中的图像识别)。完成此过程后,必须将文件移动到其他位置或删除。

我希望尽快启动我的工作,同时使多处理器功能受益于parallalise作品。

但是,我遇到了一个问题:虽然枚举目录中的文件很容易,但如何避免作业队列中的重复条目?实际上,每次枚举我的文件时,有些文件可能已排队或尚未处理。

我的第一种方法是调查System.Collections.Concurrent.*但是在添加之前没有类似乎提供了要测试的contains方法。

我也看了HashSet<string>,但我担心并发访问会遇到一些问题。

我目前的骨架是:

    private async void GetNewFiles(CancellationToken cancellationToken)
    {
        if (!cancellationToken.IsCancellationRequested)
        {
            var newfiles = Directory.GetFileSystemEntries(inputDirectory, "*.pdf", SearchOption.AllDirectories);
            logger.Trace($"{newfiles.Length} new files detected in {inputDirectory}");

            foreach (var file in newfiles)
            {
                Task.Factory.StartNew(()=>ProcessFile(file), cancellationToken);
            }

            await Task.Delay(frequency, cancellationToken);
            if (!cancellationToken.IsCancellationRequested)
            {
                GetNewFiles(cancellationToken);
           }
        }
    }

但是,此代码不会避免文件排队两次。

如果我删除Task.Delay调用并等待处理所有文件,它会起作用,但它可能导致只有一个正在运行的任务,即使添加了新文件(每次迭代处理新文件)必须在检查新文件之前完全处理。)

1 个答案:

答案 0 :(得分:1)

对您当前代码进行最少量修改的最简单方法是使用ConcurrentDictionary我认为:

private readonly ConcurrentDictionary<string, byte> _filesInProgress = new ConcurrentDictionary<string, byte>();
private async Task GetNewFiles(CancellationToken cancellationToken) {            
    if (!cancellationToken.IsCancellationRequested) {
        var newfiles = Directory.GetFileSystemEntries(inputDirectory, "*.pdf", SearchOption.AllDirectories);
        foreach (var file in newfiles) {
            // TryAdd returns true if key was not already in dictionary
            if (_filesInProgress.TryAdd(file, 0) && File.Exists(file)) {
                Task.Factory.StartNew(() => {
                    ProcessFile(file);                         
                    _filesInProgress.TryRemove(file, out _);
                }, cancellationToken);
            }
        }
        await Task.Delay(frequency, cancellationToken);
        if (!cancellationToken.IsCancellationRequested) {
            GetNewFiles(cancellationToken);
        }
    }
}

请注意,理想情况下,您需要处理线程数量有限的项目(等于核心数/虚拟核心数)。截至目前,如果您在目录中找到100个文件,您可能会产生100个线程,这些线程都是CPU密集的,因此无缘无故地相互竞争资源。