jam*_*nor 5 .net c# parallel-processing task-parallel-library
我正在尝试与使用组合创建一个任务管道/有序调度程序TaskFactory.FromAsync.
我希望能够发起Web服务请求(FromAsync使用I/O完成端口),但维护它们的顺序,并且只能在任何时候执行一个.
目前我不使用,FromAsync所以我可以做TaskFactory.StartNew(()=>api.DoSyncWebServiceCall())并依赖于OrderedTaskScheduler使用,TaskFactory以确保只有一个请求是未完成的.
我假设在使用该FromAsync方法时这种行为会保留,但它不会:
TaskFactory<Stuff> taskFactory = new TaskFactory<Stuff>(new OrderedTaskScheduler());
var t1 = taskFactory.FromAsync((a, s) => api.beginGetStuff(a, s), a => api.endGetStuff(a));
var t2 = taskFactory.FromAsync((a, s) => api.beginGetStuff(a, s), a => api.endGetStuff(a));
var t3 = taskFactory.FromAsync((a, s) => api.beginGetStuff(a, s), a => api.endGetStuff(a));
Run Code Online (Sandbox Code Playgroud)
所有这些beginGetStuff方法都在调用中被FromAsync调用(因此尽管它们按顺序被调度但是n同时发生了api调用).
FromAsync需要一个TaskScheduler 的重载:
public Task FromAsync(
IAsyncResult asyncResult,
Action<IAsyncResult> endMethod,
TaskCreationOptions creationOptions,
TaskScheduler scheduler
)
Run Code Online (Sandbox Code Playgroud)
但文档说:
TaskScheduler,用于计划执行end方法的任务.
正如你所看到的,它需要已经构建的IAsyncResult,而不是一个Func<IAsyncResult>.
这是否需要自定义FromAsync方法或我错过了什么?任何人都可以建议从这个实现开始?
干杯,
编辑:
我想从调用者那里抽象出这种行为,因此,根据TaskFactory(使用专门的TaskScheduler)行为,我需要立即返回任务 - 这个任务不仅会封装FromAsync任务,还会等待该任务的排队等待轮到执行了.
一种可能的方案:
class TaskExecutionQueue
{
private readonly OrderedTaskScheduler _orderedTaskScheduler;
private readonly TaskFactory _taskFactory;
public TaskExecutionQueue(OrderedTaskScheduler orderedTaskScheduler)
{
_orderedTaskScheduler = orderedTaskScheduler;
_taskFactory = new TaskFactory(orderedTaskScheduler);
}
public Task<TResult> QueueTask<TResult>(Func<Task<TResult>> taskGenerator)
{
return _taskFactory.StartNew(taskGenerator).Unwrap();
}
}
Run Code Online (Sandbox Code Playgroud)
但是,这会在FromAsync呼叫发生时利用线程.理想情况下,我不必这样做.
我已经决定在这里采用自定义解决方案...锁很乱且不受欢迎,但目前,这可以完成我想要的工作。
public interface ITaskExecutionQueue
{
Task<TResult> QueueTask<TResult>(Func<Task<TResult>> taskGenerator);
Task<TResult> QueueTask<TResult>(Task<Task<TResult>> taskGenerator);
int OutstandingTaskCount { get; }
event EventHandler OutstandingTaskCountChanged;
}
/// This class ensures that only a single Task is executed at any one time. They are executed sequentially in order being queued.
/// The advantages of this class over OrderedTaskScheduler is that you can use any type of Task such as FromAsync (I/O Completion ports)
/// which are not able to be scheduled using a traditional TaskScheduler.
/// Ensure that the `outer` tasks you queue are unstarted. E.g. <![CDATA[
/// _taskExeQueue.QueueTask(new Task<Task<TResult>>(() => StartMyRealTask()));
/// ]]>
class OrderedTaskExecutionQueue : ITaskExecutionQueue
{
private readonly Queue<Task> _queuedTasks = new Queue<Task>();
private Task _currentTask;
private readonly object _lockSync = new object();
/// <summary>
/// Queues a task for execution
/// </summary>
/// <typeparam name="TResult"></typeparam>
/// <param name="taskGenerator">An unstarted Task that creates your started real-work task</param>
/// <returns></returns>
public Task<TResult> QueueTask<TResult>(Func<Task<TResult>> taskGenerator)
{
return QueueTask(new Task<Task<TResult>>(taskGenerator));
}
public Task<TResult> QueueTask<TResult>(Task<Task<TResult>> taskGenerator)
{
Task<TResult> unwrapped = taskGenerator.Unwrap();
unwrapped.ContinueWith(_ =>
{
EndTask();
StartNextTaskIfQueued();
}, TaskContinuationOptions.ExecuteSynchronously);
lock (_lockSync)
{
_queuedTasks.Enqueue(taskGenerator);
if (_currentTask == null)
{
StartNextTaskIfQueued();
}
}
TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
tcs.TrySetFromTaskIncomplete(unwrapped);
OutstandingTaskCountChanged.Raise(this);
return tcs.Task;
}
private void EndTask()
{
lock (_lockSync)
{
_currentTask = null;
_queuedTasks.Dequeue();
}
OutstandingTaskCountChanged.Raise(this);
}
private void StartNextTaskIfQueued()
{
lock (_lockSync)
{
if (_queuedTasks.Count > 0)
{
_currentTask = _queuedTasks.Peek();
_currentTask.RunSynchronously();
}
}
}
/// <summary>
/// Includes the currently executing task.
/// </summary>
public int OutstandingTaskCount
{
get
{
lock (_lockSync)
{
return _queuedTasks.Count;
}
}
}
public event EventHandler OutstandingTaskCountChanged;
}
Run Code Online (Sandbox Code Playgroud)
接受未启动的任务Task<Task<TResult>>- 这允许队列决定何时执行它并开始调用FromAsync(这是内部任务)。用法:
Task<Task<TResult>> queueTask = new Task<Task<TResult>>(() => Task.Factory.FromAsync(beginAction, endAction));
Task<TResult> asyncCallTask = _taskExecutionQueue.QueueTask(queueTask);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1012 次 |
| 最近记录: |