C# 如何在不运行异步方法的情况下对其任务结果进行排队?
如果我有一个类,它包含一个稍后要执行的任务队列,并且我有一个C# 如何在不运行异步方法的情况下对其任务结果进行排队?,c#,asynchronous,task,task-queue,C#,Asynchronous,Task,Task Queue,如果我有一个类,它包含一个稍后要执行的任务队列,并且我有一个async Task方法,那么我如何在不执行它的情况下将该异步方法排队 我希望“延迟”此任务,并确保调用方看到它稍后运行,就像它在方法体中等待一样。--调用方不应该知道我已将任务排入队列以供以后使用 现在,如果我的队列已满,我将在此处构造并返回一个未运行的新任务,该任务将返回我的私有异步方法的。结果: public async Task<T> ExecuteAsync<T>(T transaction) {
async Task
方法,那么我如何在不执行它的情况下将该异步方法排队
我希望“延迟”此任务,并确保调用方看到它稍后运行,就像它在方法体中等待一样。--调用方不应该知道我已将任务排入队列以供以后使用
现在,如果我的队列已满,我将在此处构造并返回一个未运行的新任务
,该任务将返回我的私有异步方法的。结果
:
public async Task<T> ExecuteAsync<T>(T transaction) {
if (mustDelay) {
Task<T> task = new Task<T>(t => executeAsync((T) t).Result, transaction);
enqueue(task);
return await task;
}
return await executeAsync(transaction);
}
private async Task<T> executeAsync<T>(T transaction) {
await someWork();
return transaction;
}
这是否确保调用方看到的同步与从方法返回等待的结果相同
如何在不运行异步方法的情况下对其任务结果进行排队
简单回答:你不能。调用async
方法将执行该方法。它一定会开始运行。如果你想推迟通话,你需要用能做到这一点的东西来包装它
一个例子可能是Func
,除了您设计与我们共享的一点点代码表明您希望能够返回一个承诺(Task
),该承诺也代表了您将来将进行的调用。您可以将整个任务包装到另一个任务中,就像您的示例代码中一样,但这是一种非常繁重的方法,因为它只为了调用async
方法而绑定(并可能创建新的)线程池线程
更好的方法(IMHO)是使用TaskCompletionSource
。您可以在队列中存储Func
,该队列使用返回值设置TaskCompletionSource
,然后当您决定可以启动任务时,调用Func
Func<Task<T>> GetFactory()
{
return async () =>
{
DoRightAway();
await DoLater();
return DoEvenLater();
}
}
比如:
public Task<T> ExecuteAsync<T>(T transaction) {
if (mustDelay) {
TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
enqueue(async () =>
{
tcs.SetValue(await executeAsync(transaction));
});
return tcs.Task;
}
return executeAsync(transaction);
}
公共任务执行同步(T事务){
如果(必须延迟){
TaskCompletionSource tcs=新的TaskCompletionSource();
排队(异步()=>
{
设置值(等待执行同步(事务));
});
返回tcs.Task;
}
返回executeAsync(事务);
}
注意,这里不需要将ExecuteAsync()
设置为async
。您可以返回TaskCompletionSource
的任务,也可以返回executeAsync()
方法返回的任务(顺便说一句,两个方法的名称仅在字母大小写上不同,这是一个可怕的想法)
还要注意,您的队列将存储Func
对象,甚至可能存储Action
对象(通常不支持async void
方法,例如上面的匿名方法,但您首先没有显示任何异常处理,因此在这种情况下,您可能会发现它可以正常工作)。当您将一个项目出列时,您将调用该委托。根据您的需要,这可能是“触发并忘记”(如果您存储操作
委托),也可能是退出队列并调用委托的方法可能会等待委托的返回值(如果您存储Func
委托)
不幸的是,你的问题相当模糊。所以不可能提供比这更多的东西。如果您需要其他帮助,请改进问题,使其包含一个好的答案,清楚地显示您正在努力完成的任务以及您在具体理解中遇到的困难,并提供适当的解释来详细描述。以下是一个简化的示例:
async Task<T> GetResult<T>()
{
DoRightAway();
await DoLater();
return DoEvenLater();
}
Task<T> task = GetResult<T>();
//task is running or complete
我们调用此新方法来获取函数:
Func<Task<T>> function = GetFactory();
Task<T> task = function();
这可能会有所帮助:
我有一个类,它包含一个稍后要执行的任务队列,
这就是您需要解决的问题。不要那样做。@ThomasChristof,这样做充其量会造成一个问题的世界,在最坏的情况下根本不起作用;运行async
方法会返回一个“热”任务;也就是说,任务已经启动,并且已经运行到等待未完成等待的第一个等待点。如果要将方法的操作推迟到以后,通常需要将其包装在Func
中(也可以将Func
包装在Lazy
中),然后等待调用Func
。简言之:不要混淆“我想要一个按需计算的值”单子--Func
“我想要一个按需计算并缓存的值”monad--Lazy--
“我想要一个将来计算的值,当它准备好时会调用我“--任务
--monad。很明显,它们都是相似的,它们都是相互组成的,但是它们在逻辑上是不同的。您好,谢谢您的回答。我已经采纳了您的建议和@EricLippert的评论:我正在重构实现,以容纳Func,并将在TaskScheduler上包装和执行Func,而将“延迟”任务排入队列的工作将在Scheduler上进行。这样API就更有意义了(消费者将只提供一个Func,而实现将它异步,而不是从消费者那里“一直异步”)。
Task<T> task = function();
T result = await task;