如何在不运行异步方法的情况下排队异步方法的任务结果?

时间:2017-10-23 18:45:34

标签: c# asynchronous task task-queue

如果我有一个类,其中包含稍后要执行的任务队列,并且我有一个async Task<T>方法,如何在不执行它的情况下将该异步方法排入队列?

我想&#34;延迟&#34;这个任务,并确保调用者看到它稍后运行,就像在方法体中等待它一样。 ---来电者不应该知道我已将任务排入队列。

现在,如果我的队列已满,我构建并返回一个未运行的新Task<T>,这将返回我的私有异步方法的.Result

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;
}

当其他任务完成时,我将队列和Start()排队并执行任务:

dequeuedTask.Start();

这是否确保调用者看到相同的同步,就像从方法中返回等待的结果一样?

1 个答案:

答案 0 :(得分:-2)

  

如何在不运行异步方法的情况下对异步方法的任务结果进行排队?

简短回答:你不能。调用async方法会执行该方法。它必然开始运行。如果您希望能够推迟呼叫,则需要将其包装在可以执行此操作的内容中。

一个例子可能是Func<Task<T>>,除了你设计与我们分享的一点点代码表明你希望能够返回一个承诺(Task<T>)以及代表此调用的承诺你将来会做出来的。你可以将整个事情包装在另一个任务中,比如在你的示例代码中,但恕我直言,这是一个非常严厉的方法,因为它绑定(并可能创建新的)线程池线程只是为了调用async方法。

更好的方法(恕我直言)要做到这一点就是使用TaskCompletionSource<T>。您可以在队列中存储Func<Task>,使用返回值设置TaskCompletionSource<T>,然后当您决定可以启动任务时,调用Func<Task>

类似的东西:

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);
}

请注意,此处ExecuteAsync<T>()不需要async。您要么返回TaskCompletionSource<T>的任务,要么返回executeAsync<T>()方法返回的任务(顺便说一句,有两种名称只有字母大小不同的方法是恕我直言<强>可怕的想法)。

另请注意,您的队列将存储Func<Task>个对象,甚至可能存储Action个对象(它通常不赞成async void方法,例如上面的匿名方法,但是您首先没有显示任何异常处理,所以也许你会发现它在这种情况下工作正常)。当您将项目出列时,您将调用该委托。根据您的需要,这将是“即发即忘”#34; (如果存储Action个委托)或者出列并调用委托的方法可以等待委托的返回值(如果要存储Func<Task>个委托)。

不幸的是,你的问题相当含糊。所以不可能提供比这更多的东西。如果您需要其他帮助,请改进问题,以便它包含一个好的Minimal, Complete, and Verifiable code example,清楚地显示您正在尝试完成的内容以及您在查找时遇到的具体问题以及相应的解释详细描述。