如何在没有 运行 的情况下对异步方法的任务结果进行排队?
How can I queue the Task result of an async method without running it?
如果我有一个 class 保存稍后要执行的任务队列,并且我有一个 async Task<T>
方法,我如何在不执行该异步方法的情况下将其入队?
我想要 "delay" 这个任务,并确保调用者稍后看到它 运行 就像它在方法主体中等待一样。 --- 来电者应该不知道我已将任务排入队列以备后用。
现在,如果我的队列已满,我会在此处构建并 return 新建一个 Task<T>
,它不是 运行ning,而是 returning .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();
这是否确保调用者看到与 return 方法中等待的结果相同的同步?
How can I queue the Task result of an async method without running it?
简答:你不能。调用 async
方法会执行该方法。它必然 将 开始 运行。如果您希望能够延迟调用,则需要将其包装在可以执行此操作的内容中。
一个例子可能是 Func<Task<T>>
,除了您屈尊与我们分享的一小段代码表明您希望能够 return 一个承诺 (Task<T>
)也代表你将来会打这个电话。您可以将整个事情包装在另一个任务中,就像在您的示例代码中一样,但是恕我直言,这是一种非常笨拙的方法,因为它绑定(并可能创建新的)线程池线程只是为了调用 async
方法.
一个更好的方法(恕我直言)是使用 TaskCompletionSource<T>
。您可以在队列中存储一个 Func<Task>
,它使用 return 值来设置 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
。您要么 return TaskCompletionSource<T>
的任务,要么 return 由 executeAsync<T>()
方法编辑的任务(顺便说一句,有两个方法的名称仅在字母上有所不同-套管是恕我直言 可怕的 想法)。
另请注意,您的队列将存储 Func<Task>
个对象,甚至可能 Action
个对象(它通常不赞成 async void
方法,例如上面的匿名方法,但您没有首先不显示任何异常处理,所以也许你会发现它在这种情况下工作正常)。当您使一个项目出队时,您将调用该委托。根据您的需要,这将是 "fire-and-forget"(如果您存储 Action
委托)或者使委托出列和调用的方法可能等待委托的 return 值(如果您是存储 Func<Task>
个代表)。
很遗憾,您的问题相当含糊。所以不可能提供比这更多的东西。如果您需要其他帮助,请改进问题,使其包含一个很好的 Minimal, Complete, and Verifiable code example ,清楚地显示您正在尝试完成什么以及您具体难以弄清楚什么,以及适当的解释来描述它细节。
这是一个简化的例子:
async Task<T> GetResult<T>()
{
DoRightAway();
await DoLater();
return DoEvenLater();
}
Task<T> task = GetResult<T>();
//task is running or complete
您想通过获取它代表的未完成任务来推迟 GetResult<T>()
的执行。问题是调用 async
方法会启动 运行 调用它,即使您没有await
调用也是如此。方法运行s到第一个await
,在这个例子中是await DoLater()
。
要理解为什么不 运行 就无法获得 async
方法所代表的任务,请考虑以下三个概念:
- A
Func
object 代表一个未开始的计算。
- 一个新的
Lazy
object 表示一个未开始的计算,它将 运行 一次。
- 对
async
方法的调用表示最终计算。
这些概念中的每一个都是不同的,但它们可以相互组合成组合表示。我们的 GetResult<T>()
调用是第三个概念的示例。当结果准备好时,它将通过 await
或 ContinueWith
继续执行。在我们的例子中,我们甚至不想开始执行。相反,我们需要将第一个和第三个概念组合成一个表示。也就是说,我们需要一个 unstarted 计算 (Func
),当它最终开始时(async
方法调用),最终 完成。
将这种见解付诸实践,我们通过将 GetResult<T>()
的 body 包装在 Func
.
中解决了我们的问题
Func<Task<T>> GetFactory()
{
return async () =>
{
DoRightAway();
await DoLater();
return DoEvenLater();
}
}
我们调用这个新方法来获取函数:
Func<Task<T>> function = GetFactory();
恭喜!我们没有 运行ning 就完成了任务!好吧,有点。我们有一个包装(尚未创建)任务的函数。当准备好创建和 运行 任务时,我们调用函数:
Task<T> task = function();
我们确保任务完成并得到结果:
T result = await task;
如果我有一个 class 保存稍后要执行的任务队列,并且我有一个 async Task<T>
方法,我如何在不执行该异步方法的情况下将其入队?
我想要 "delay" 这个任务,并确保调用者稍后看到它 运行 就像它在方法主体中等待一样。 --- 来电者应该不知道我已将任务排入队列以备后用。
现在,如果我的队列已满,我会在此处构建并 return 新建一个 Task<T>
,它不是 运行ning,而是 returning .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();
这是否确保调用者看到与 return 方法中等待的结果相同的同步?
How can I queue the Task result of an async method without running it?
简答:你不能。调用 async
方法会执行该方法。它必然 将 开始 运行。如果您希望能够延迟调用,则需要将其包装在可以执行此操作的内容中。
一个例子可能是 Func<Task<T>>
,除了您屈尊与我们分享的一小段代码表明您希望能够 return 一个承诺 (Task<T>
)也代表你将来会打这个电话。您可以将整个事情包装在另一个任务中,就像在您的示例代码中一样,但是恕我直言,这是一种非常笨拙的方法,因为它绑定(并可能创建新的)线程池线程只是为了调用 async
方法.
一个更好的方法(恕我直言)是使用 TaskCompletionSource<T>
。您可以在队列中存储一个 Func<Task>
,它使用 return 值来设置 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
。您要么 return TaskCompletionSource<T>
的任务,要么 return 由 executeAsync<T>()
方法编辑的任务(顺便说一句,有两个方法的名称仅在字母上有所不同-套管是恕我直言 可怕的 想法)。
另请注意,您的队列将存储 Func<Task>
个对象,甚至可能 Action
个对象(它通常不赞成 async void
方法,例如上面的匿名方法,但您没有首先不显示任何异常处理,所以也许你会发现它在这种情况下工作正常)。当您使一个项目出队时,您将调用该委托。根据您的需要,这将是 "fire-and-forget"(如果您存储 Action
委托)或者使委托出列和调用的方法可能等待委托的 return 值(如果您是存储 Func<Task>
个代表)。
很遗憾,您的问题相当含糊。所以不可能提供比这更多的东西。如果您需要其他帮助,请改进问题,使其包含一个很好的 Minimal, Complete, and Verifiable code example ,清楚地显示您正在尝试完成什么以及您具体难以弄清楚什么,以及适当的解释来描述它细节。
这是一个简化的例子:
async Task<T> GetResult<T>()
{
DoRightAway();
await DoLater();
return DoEvenLater();
}
Task<T> task = GetResult<T>();
//task is running or complete
您想通过获取它代表的未完成任务来推迟 GetResult<T>()
的执行。问题是调用 async
方法会启动 运行 调用它,即使您没有await
调用也是如此。方法运行s到第一个await
,在这个例子中是await DoLater()
。
要理解为什么不 运行 就无法获得 async
方法所代表的任务,请考虑以下三个概念:
- A
Func
object 代表一个未开始的计算。 - 一个新的
Lazy
object 表示一个未开始的计算,它将 运行 一次。 - 对
async
方法的调用表示最终计算。
这些概念中的每一个都是不同的,但它们可以相互组合成组合表示。我们的 GetResult<T>()
调用是第三个概念的示例。当结果准备好时,它将通过 await
或 ContinueWith
继续执行。在我们的例子中,我们甚至不想开始执行。相反,我们需要将第一个和第三个概念组合成一个表示。也就是说,我们需要一个 unstarted 计算 (Func
),当它最终开始时(async
方法调用),最终 完成。
将这种见解付诸实践,我们通过将 GetResult<T>()
的 body 包装在 Func
.
Func<Task<T>> GetFactory()
{
return async () =>
{
DoRightAway();
await DoLater();
return DoEvenLater();
}
}
我们调用这个新方法来获取函数:
Func<Task<T>> function = GetFactory();
恭喜!我们没有 运行ning 就完成了任务!好吧,有点。我们有一个包装(尚未创建)任务的函数。当准备好创建和 运行 任务时,我们调用函数:
Task<T> task = function();
我们确保任务完成并得到结果:
T result = await task;