我有以下情况
其中一个要求是能够优雅地阻止消费者(立即或让正在进行的流程完成)。
我沿着这条路走了一段路:
while (processing)
{
//limits number of concurrent tasks
_processingSemaphore.Wait(queueCancellationToken);
//Take next job when available or wait for cancel signal
currentwork = workQueue.Take(taskCancellationToken);
//check that it can actually process this work
if (CanProcess(currnetWork)
{
var task = CreateTask(currentwork)
task.ContinueWith((t) => { //release processing slot });
}
else
//release slot, return job? something else?
}
取消令牌来源是来电者代码,可以取消。有两个是为了能够在不取消正在运行的任务的情况下停止排队。
我厌倦了将“队列”实现为BlockingCollection包装“安全”的SortedSet。一般的想法工作(按时间排序),除了我需要找到一个匹配约束的新工作的情况。如果我将作业返回队列并尝试再次使用,我将获得相同的作业。
可以从队列中取出作业,直到找到合适的作业,然后返回“非法”作业,但这可能会导致其他消费者处理无序作业的问题
另一种选择是传递一个简单的集合和一种锁定它的方法,然后根据当前的约束锁定并进行简单的搜索。同样,这意味着编写可能不是线程安全的代码。
任何其他可以提供帮助的建议/指针/数据结构?
答案 0 :(得分:0)
我会用TPL Dataflow实现您的要求。看看你可以用Producer-Consumer pattern实现它的方式。我相信这将满足您的所有要求(包括取消消费者)。
编辑(对于那些不喜欢阅读文档的人,但是谁... ...
以下是如何使用TPL Dataflow实现要求的示例。这种实现的优点在于,消费者不会绑定到单个线程,只在需要处理数据时才使用池线程。
static void Main(string[] args)
{
BufferBlock<string> source = new BufferBlock<string>();
var cancellation = new CancellationTokenSource();
LinkConsumer(source, "A", cancellation.Token);
LinkConsumer(source, "B", cancellation.Token);
LinkConsumer(source, "C", cancellation.Token);
// Link an action that will process source values that are not processed by other
source.LinkTo(new ActionBlock<string>((s) => Console.WriteLine("Default action")));
while (cancellation.IsCancellationRequested == false)
{
ConsoleKey key = Console.ReadKey(true).Key;
switch (key)
{
case ConsoleKey.Escape:
cancellation.Cancel();
break;
default:
Console.WriteLine("Posted value {0} on thread {1}.", key, Thread.CurrentThread.ManagedThreadId);
source.Post(key.ToString());
break;
}
}
source.Complete();
Console.WriteLine("Done.");
Console.ReadLine();
}
private static void LinkConsumer(ISourceBlock<string> source, string prefix, CancellationToken token)
{
// Link a consumer that will buffer and process all input of the specified prefix
var consumer = new ActionBlock<string>(new Action<string>(Process), new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = 1, SingleProducerConstrained = true, CancellationToken = token, TaskScheduler = TaskScheduler.Default });
var linkDisposable = source.LinkTo(consumer, (p) => p == prefix);
// Dispose the link (remove the link) when cancellation is requested.
token.Register(linkDisposable.Dispose);
}
private static void Process(string arg)
{
Console.WriteLine("Processed value {0} in thread {1}", arg, Thread.CurrentThread.ManagedThreadId);
// Simulate work
Thread.Sleep(500);
}
答案 1 :(得分:0)
我认为Hans是对的:如果你已经有一个线程安全的SortedSet(实现IProducerConsumerCollection
,那么它可以在BlockingCollection
中使用),那么你所需要的只是放置那些文件现在可以处理到集合中。如果您完成了一个使另一个文件可供处理的文件,则此时将其他文件添加到集合中,而不是更早。