6

タスクスケジューラを作成しているので、タスクを受け入れて待機するある種の繰り返し関数を作成しようとしていますが、奇妙な例外が発生します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);
}

それで、関数がTask、Task、bool、またはその他のタイプを受け入れるようにしたいので、その一般的なTask cuzをどのように待つことができるか教えてもらえますか?

4

3 に答える 3

8

このために長時間実行されるタスクを開始する必要はありません。メソッドを直接非同期にするだけです。

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

これにより、Actionが現在のコンテキストで実行されます。それが必要ない場合は、次を使用できます。

await Task.Delay(pollInterval, token).ConfigureAwait(false);
action();

これにより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;
        }
    }
}

編集2:

ラムダを渡したい場合はasync、メソッドが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();
    }
}

チャットに応じて編集:

ポーリングしたいが、操作の結果を使用する場合は、次を使用できます。

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

次に、次の方法でこれを呼び出すことができます。

RunAtIntervalAsync(TimeSpan.FromSeconds(1), 
   async () => { await Task.Delay(1000); return "Foo"; },
   result => UpdateUI(result),
   token);
于 2013-07-22T20:08:14.663 に答える