框架中是否有某些内容允许我异步执行委托队列?
我的意思是,我希望代表按照排队顺序一次执行一个,但我希望整个过程以异步方式运行。队列也没有固定,会定期添加其他委托,一旦到达队列顶部就应该处理。
我不需要特别使用Queue
,而是我将如何描述所需的行为。
我可以自己写一些东西去做,但如果有内置的东西,我可以使用,而不是更好。
我简要地查看了ThreadPool.QueueUserWorkItem
,因为它允许按顺序执行,但是可以找到一种令人满意的方法来防止一次执行多个。
答案 0 :(得分:4)
框架中是否有允许我这样做的东西 异步执行委托队列?
我将其实现为自定义任务调度程序。然后,您可以将您的委托作为任务排队并运行,这将为您提供异常处理,取消和async/await
的所有好处。
使用BlockingCollection
实现一个以序列顺序执行代理的任务调度程序非常简单。以下SerialTaskScheduler
是Stephen Toub's StaTaskScheduler
的简化版:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Console_21628490
{
// Test
class Program
{
static async Task DoWorkAsync()
{
using (var scheduler = new SerialTaskScheduler())
{
var tasks = Enumerable.Range(1, 10).Select(i =>
scheduler.Run(() =>
{
var sleep = 1000 / i;
Thread.Sleep(sleep);
Console.WriteLine("Task #" + i + ", sleep: " + sleep);
}, CancellationToken.None));
await Task.WhenAll(tasks);
}
}
static void Main(string[] args)
{
DoWorkAsync().Wait();
Console.ReadLine();
}
}
// SerialTaskScheduler
public sealed class SerialTaskScheduler : TaskScheduler, IDisposable
{
Task _schedulerTask;
BlockingCollection<Task> _tasks;
Thread _schedulerThread;
public SerialTaskScheduler()
{
_tasks = new BlockingCollection<Task>();
_schedulerTask = Task.Run(() =>
{
_schedulerThread = Thread.CurrentThread;
foreach (var task in _tasks.GetConsumingEnumerable())
TryExecuteTask(task);
});
}
protected override void QueueTask(Task task)
{
_tasks.Add(task);
}
protected override IEnumerable<Task> GetScheduledTasks()
{
return _tasks.ToArray();
}
protected override bool TryExecuteTaskInline(
Task task, bool taskWasPreviouslyQueued)
{
return _schedulerThread == Thread.CurrentThread &&
TryExecuteTask(task);
}
public override int MaximumConcurrencyLevel
{
get { return 1; }
}
public void Dispose()
{
if (_schedulerTask != null)
{
_tasks.CompleteAdding();
_schedulerTask.Wait();
_tasks.Dispose();
_tasks = null;
_schedulerTask = null;
}
}
public Task Run(Action action, CancellationToken token)
{
return Task.Factory.StartNew(action, token, TaskCreationOptions.None, this);
}
public Task Run(Func<Task> action, CancellationToken token)
{
return Task.Factory.StartNew(action, token, TaskCreationOptions.None, this).Unwrap();
}
public Task<T> Run<T>(Func<Task<T>> action, CancellationToken token)
{
return Task.Factory.StartNew(action, token, TaskCreationOptions.None, this).Unwrap();
}
}
}
输出:
Task #1, sleep: 1000 Task #2, sleep: 500 Task #3, sleep: 333 Task #4, sleep: 250 Task #5, sleep: 200 Task #6, sleep: 166 Task #7, sleep: 142 Task #8, sleep: 125 Task #9, sleep: 111 Task #10, sleep: 100
答案 1 :(得分:2)
您可以使用TPL dataflow
的ActionBlock
,只需排列一个包含Delegate
的类和一系列参数。 ActionBlock
将一次只执行一个代理。
var block = new ActionBlock<Item>(_ => _.Action.DynamicInvoke(_.Paramters));
class Item
{
public Delegate Action { get; private set; }
public object[] Parameters { get; private set; }
public Item(Delegate action, object[] parameters)
{
Action = action;
Parameters = parameters;
}
}
更简单的选择是使用ActionBlock
Action
,但这会强制您捕获参数:
var block = new ActionBlock<Action>(action => action());
block.Post(() => Console.WriteLine(message));