类型'T'不等待

Rom*_*key 5 c# task task-parallel-library async-await

我正在创建一个任务调度程序,所以我试图做一些接受任务并等待它的重复函数,但我得到一个奇怪的异常 Type 'T' is not awaitable

public static Task<T> Interval<T>(TimeSpan pollInterval, Func<T> action, CancellationToken token)
{
    return Task.Factory.StartNew(
        async () =>
        {
            for (; ; )
            {
                if (token.WaitCancellationRequested(pollInterval))
                    break;

                await action();
            }
        }, token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
Run Code Online (Sandbox Code Playgroud)

所以任何人都可以告诉我,我怎么能等待一个通用的任务因为我希望该功能接受任何任务,任务,布尔或​​任何其他类型?

Ree*_*sey 8

您不需要为此启动长时间运行的任务 - 只需使您的方法直接异步:

public static async Task RunAtIntervalAsync(TimeSpan pollInterval, Action action, CancellationToken token)
{
    while(true)
    {
        await Task.Delay(pollInterval, token);
        action();
    }
}
Run Code Online (Sandbox Code Playgroud)

这将导致Action在当前上下文中运行.如果不需要,您可以使用:

await Task.Delay(pollInterval, token).ConfigureAwait(false);
action();
Run Code Online (Sandbox Code Playgroud)

这将导致Action不在调用者的同一同步上下文上运行,并可能使用ThreadPool线程.


编辑以回应评论:

如果您不希望结果任务返回取消,但只是在激活令牌时返回,您可以使用:

public static async Task RunAtIntervalAsync(TimeSpan pollInterval, Action action, CancellationToken token)
{
    while(!token.IsCancellationRequested)
    {
        try
        {
            await Task.Delay(pollInterval, token);
            action();
        }
        catch(OperationCanceledException e)
        {
            // Swallow cancellation - dangerous if action() throws this, though....
            break;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑2:

如果你想传入asynclambdas,你应该让方法采用Func<Task>,而不是Action:

public static async Task RunAtIntervalAsync(TimeSpan pollInterval, Func<Task> actionTask, CancellationToken token)
{
    while(!token.IsCancellationRequested)
    {
        try
        {
            await Task.Delay(pollInterval, token);
        }
        catch(OperationCanceledException e)
        {
            // Swallow cancellation
            break;
        }

        await actionTask();
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑以回应聊天:

如果您想轮询,但使用操作的结果,您可以使用:

public static async Task RunAtIntervalAsync<T>(TimeSpan pollInterval, Func<Task<T>> fetchOperation, Action<T> operationOnResult, CancellationToken token)
{
    while(!token.IsCancellationRequested)
    {
        try
        {
            await Task.Delay(pollInterval, token);
        }
        catch(OperationCanceledException e)
        {
            // Swallow cancellation
            break;
        }

        // Get a value
        T value = await fetchOperation();

        // Use result (ie: update UI)
        operationOnResult(value);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以通过以下方式调用:

RunAtIntervalAsync(TimeSpan.FromSeconds(1), 
   async () => { await Task.Delay(1000); return "Foo"; },
   result => UpdateUI(result),
   token);
Run Code Online (Sandbox Code Playgroud)


SLa*_*aks 5

你不能.

你可以创建一个带有泛型异步函数的函数 - 一个返回a的函数Task<T>.
那将是一个Func<Task<T>>.

您还可以创建一个具有通用同步功能的功能,这是您现在拥有的功能.

您不能创建一个可以执行任何操作的函数,但是您可以进行两次重载.


在一个不相关的注释中,您的函数实际上从未使用函数的返回值.
因此,你根本不应该把它变成通用的; 你应该取一个Func<Task>或一个Action.