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

Ste*_*oco 5 c# asynchronous task task-queue

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

我想“延迟”此任务,并确保调用者看到它稍后运行,就像在方法体中等待它一样。--- 调用者不应该知道我已将任务排队以供稍后使用。

现在,如果我的队列已满,我会构造并返回一个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;
}
Run Code Online (Sandbox Code Playgroud)

当其他任务完成时,我使Start()该任务出队:

dequeuedTask.Start();
Run Code Online (Sandbox Code Playgroud)

这是否确保调用者看到与仅从方法返回等待结果相同的同步?

Pet*_*iho 2

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

简短的回答:你不能。调用一个async方法就会执行该方法。它必然会开始运行。如果您希望能够推迟调用,则需要将其包装在可以实现此目的的东西中。

一个例子可能是Func<Task<T>>,除了您打算与我们分享的一小段代码表明您还希望能够返回Task<T>一个代表您将来将进行的调用的承诺()。您可以将整个事情包装在另一个任务中,就像在示例代码中一样,但恕我直言,这是一种相当严厉的方法,因为它只是为了调用该方法而绑定(并可能创建新的)线程池线程async

实现这一点的更好方法(恕我直言)是使用TaskCompletionSource<T>. 您可以将 a 存储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);
}
Run Code Online (Sandbox Code Playgroud)

请注意,这里不需要ExecuteAsync<T>()be async。您要么返回 的TaskCompletionSource<T>任务,要么返回该executeAsync<T>()方法返回的任务(顺便说一句,两个方法的名称仅在字母大小写上有所不同,恕我直言,这是一个可怕的想法)。

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

不幸的是,你的问题相当模糊。因此不可能提供比这更多的东西。如果您需要其他帮助,请改进问题,使其包含一个良好的最小、完整和可验证的代码示例,清楚地显示您想要完成的任务以及您具体难以弄清楚的内容,并提供适当的解释详细描述一下。