检测DataFlow

时间:2017-03-16 13:20:53

标签: c# multithreading tpl-dataflow dataflow

我一直在构建一个使用Queue<string>对象处理文件的服务来管理项目。

public partial class BasicQueueService : ServiceBase
{
    private readonly EventWaitHandle completeHandle = 
      new EventWaitHandle(false, EventResetMode.ManualReset, "ThreadCompleters");

    public BasicQueueService()
    {
        QueueManager = new Queue<string>();
    }

    public bool Stopping { get; set; }

    private Queue<string> QueueManager { get; }

    protected override void OnStart(string[] args)
    {
        Stopping = false;

        ProcessFiles();
    }

    protected override void OnStop()
    {
        Stopping = true;
    }

    private void ProcessFiles()
    {
        while (!Stopping)
        {
            var count = QueueManager.Count;
            for (var i = 0; i < count; i++)
            {
                //Check the Stopping Variable again.
                if (Stopping) break;

                var fileName = QueueManager.Dequeue();
                if (string.IsNullOrWhiteSpace(fileName) || !File.Exists(fileName)) 
                       continue;

                Console.WriteLine($"Processing {fileName}");

                Task.Run(() =>
                    {
                        DoWork(fileName);
                    })
                    .ContinueWith(ThreadComplete);
            }
            if (Stopping) continue;

            Console.WriteLine("Waiting for thread to finish, or 1 minute.");
            completeHandle.WaitOne(new TimeSpan(0, 0, 15));
            completeHandle.Reset();
        }
    }

    partial void DoWork(string fileName);

    private void ThreadComplete(Task task)
    {
        completeHandle.Set();
    }

    public void AddToQueue(string file)
    {
        //Called by FileWatcher/Manual classes, not included for brevity.
        lock (QueueManager)
        {
            if (QueueManager.Contains(file)) return;

            QueueManager.Enqueue(file);
        }
    }
}

在研究如何限制线程数量的同时(我尝试了一个递增int的手动类,但是在我的代码中没有正确递减的问题),我遇到了TPL DataFlow,它似乎更适合我想要实现的目标 - 具体来说,它允许我让框架处理线程/排队等。

这是我的服务:

public partial class BasicDataFlowService : ServiceBase
{
    private readonly ActionBlock<string> workerBlock;

    public BasicDataFlowService()
    {
        workerBlock = new ActionBlock<string>(file => DoWork(file), new ExecutionDataflowBlockOptions()
        {
            MaxDegreeOfParallelism = 32
        });
    }

    public bool Stopping { get; set; }

    protected override void OnStart(string[] args)
    {
        Stopping = false;
    }

    protected override void OnStop()
    {
        Stopping = true;
    }

    partial void DoWork(string fileName);

    private void AddToDataFlow(string file)
    {
        workerBlock.Post(file);
    }
}

这很有效。但是,我想确保文件只被添加到TPL DataFlow一次。使用Queue,我可以使用.Contains()进行检查。我可以使用TPL DataFlow吗?

2 个答案:

答案 0 :(得分:1)

只有当文件在短时间内两次进入您的服务时,Queue的解决方案才有效。如果它再次出现,比如几个小时,队列就不会包含它,就像你Dequeue那样。

如果需要此解决方案,那么您可以使用MemoryCache来存储已处理的文件路径,如下所示:

using System.Runtime.Caching;

private static object _lock = new object();

private void AddToDataFlow(string file)
{
    lock (_lock)
    {
        if (MemoryCache.Default.Contains(file))
        {
            return;
        }

        // no matter what to put into the cache
        MemoryCache.Default[file] = true;
    // we can now exit the lock
    }

    workerBlock.Post(file);
}

但是,如果您的应用程序必须运行很长时间(该服务打算这样做),您最终将耗尽内存。在这种情况下,您可能需要将文件路径存储在数据库或其他内容中,因此即使重新启动服务,您的代码也会恢复状态。

答案 1 :(得分:0)

您可以在DoWork内查看。

您必须保存Hash已经工作的项目并检查哈希中是否存在当前文件名。