我一直在构建一个使用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
吗?
答案 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
已经工作的项目并检查哈希中是否存在当前文件名。