Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/spring-mvc/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何让任务提前完成_C#_Asynchronous_Delay - Fatal编程技术网

C# 如何让任务提前完成

C# 如何让任务提前完成,c#,asynchronous,delay,C#,Asynchronous,Delay,我的代码正在等待一段时间延迟: await Task.Delay(10_000, cancellationToken); 在某些情况下,即使超时尚未过期,我也希望立即继续(“缩短延迟”)。此决定可在代码已在等待延迟时作出 我不想做的事情:按条件循环 foreach (var i = 0; i < 10 && !continueImmediately; i++) { await Task.Delay(1000, cancellationToken); } 因为我想

我的代码正在等待一段时间延迟:

await Task.Delay(10_000, cancellationToken);
在某些情况下,即使超时尚未过期,我也希望立即继续(“缩短延迟”)。此决定可在代码已在等待延迟时作出

我不想做的事情:按条件循环

foreach (var i = 0; i < 10 && !continueImmediately; i++)
{
    await Task.Delay(1000, cancellationToken);
}
因为我想避免例外

我想到了什么:

TaskCompletionSource tcs = new TaskCompletionSource();
await Task.WhenAny(new[] { tcs.Task, Task.Delay(10_000, cancellationToken) });
要缩短延迟时间:
tcs.SetResult()

这似乎有效,但我不确定是否有任何清理遗漏。例如,如果采取了快捷方式(即由于
tcs.Task
而完成的时间),则
Task.Delay
是否会继续消耗资源

注意:我当然喜欢取消支持,但这不是我的问题,因此您可以忽略我问题中的所有取消令牌。

您可以使用不传播异常的自定义:

public struct SuppressException : ICriticalNotifyCompletion
{
    private Task _task;
    private bool _continueOnCapturedContext;

    /// <summary>
    /// Returns an awaiter that doesn't propagate the exception or cancellation
    /// of the task. The awaiter's result is true if the task completes
    /// successfully; otherwise, false.
    /// </summary>
    public static SuppressException Await(Task task,
        bool continueOnCapturedContext = true) => new SuppressException
        { _task = task, _continueOnCapturedContext = continueOnCapturedContext };

    public SuppressException GetAwaiter() => this;
    public bool IsCompleted => _task.IsCompleted;
    public void OnCompleted(Action action) => _task.ConfigureAwait(
        _continueOnCapturedContext).GetAwaiter().OnCompleted(action);
    public void UnsafeOnCompleted(Action action) => _task.ConfigureAwait(
        _continueOnCapturedContext).GetAwaiter().UnsafeOnCompleted(action);
    public bool GetResult() => _task.Status == TaskStatus.RanToCompletion;
}

上面的
SuppressException
struct是裸体实现的一个稍微增强的版本,可以在GitHub post(由Stephen Toub发布)中找到。您也可以在这个答案的后面找到它。

您可以使用自己的延迟方法:

public class DelayTask
{
    private Timer timer;
    private TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
    private CancellationTokenRegistration registration;
    private int lockObj = 0;

    private DelayTask(TimeSpan delay, CancellationToken cancel)
    {
        timer = new Timer(OnElapsed, null, delay, Timeout.InfiniteTimeSpan);
        registration = cancel.Register(OnCancel);
    }

    public static Task Delay(TimeSpan delay, CancellationToken cancel) => new DelayTask(delay, cancel).tcs.Task;

    private void OnCancel() => SetResult(false);
    private void OnElapsed(object state) => SetResult(true);
    private void SetResult( bool completed)
    {
        if (Interlocked.Exchange(ref lockObj, 1) == 0)
        {
            tcs.SetResult(completed);
            timer.Dispose();
            registration.Dispose();
        }
    }
}
公共类延迟任务
{
私人定时器;
私有TaskCompletionSource tcs=新TaskCompletionSource();
私人取消登记;
私有int lockObj=0;
专用延迟任务(时间跨度延迟、取消令牌取消)
{
计时器=新的计时器(失效、空、延迟、超时.InfiniteTimeSpan);
注册=取消。注册(OnCancel);
}
公共静态任务延迟(TimeSpan Delay,CancellationToken cancel)=>新建延迟任务(Delay,cancel).tcs.Task;
私有void OnCancel()=>SetResult(false);
private void onelassed(对象状态)=>SetResult(true);
私有void SetResult(bool已完成)
{
if(联锁交换(参考锁定对象,1)==0)
{
tcs.SetResult(已完成);
timer.Dispose();
注册。处置();
}
}
}
据我所知,这与任务基本相同。延迟,但如果完成或未完成,则返回bool。请注意,它完全未经测试

这似乎有效,但我不确定是否有任何清理遗漏。例如,如果采取了快捷方式(即,由于tcs.Task,任务何时完成),任务延迟是否会继续消耗资源


可能不会,看吧。在大多数情况下,不需要处理任务。在最坏的情况下,由于需要最终确定,它们将导致一些性能损失。即使Task.Delay是在以后某个时间触发的,对性能的影响也应该是最小的。

您可以编写自己的
Delay()
,它不会抛出在
Task.Delay()
实现后建模的异常方法

下面是一个示例,如果取消令牌被取消,则返回
true
;如果没有取消令牌,则返回
false
(延迟正常超时)

这会打印类似于在00:00:02.0132319之后返回True的

如果您这样更改超时:

const int CANCEL1_TIMEOUT =  5000;
const int CANCEL2_TIMEOUT =  3000;
const int DELAY_TIMEOUT   =  2000;
结果类似于00:00:02.0188434之后返回False的



作为参考,对于
Task.Delay()

为什么您认为
Task.Delay
会“运行”?它的目的是允许一个任务在不占用线程的情况下,在以后发出信号pointlessly@Damien_The_Unbeliever好的,“运行”是一个错误的术语,但我的观点是:它仍然会消耗资源吗?你以这样或那样的方式取消,那么另一个CTS和
任务的意义是什么。什么时候有?如果不想等待,请不要调用
Task.Delay()
If(!shortcut){wait Task.Delay();}
。如果您想让步,请使用
wait Task.yield()
,例如
wait shortcut?Task.yield():Task.Delay(1000)
@PanagiotisKanavos在我的问题中更清楚地表明,在代码已经等待延迟的情况下,可能会做出选择捷径的决定。您是否试图避免取消引发的异常?您可以通过忽略异常的延续来实现这一点,例如
Task.Delay(…).ContinueWith(=>{})。谢谢你的链接,有趣的阅读!向上投票。顺便说一句,
tokenSource2
是冗余的。您可以直接
取消
compositeTokenSource
。效果非常好-也谢谢您,不用使用
任务。运行
+
线程。Sleep
组合来计划取消源代码,您可以改为使用以下方法:
令牌源2.CancelAfter(CANCEL2\u超时)
public class DelayTask
{
    private Timer timer;
    private TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
    private CancellationTokenRegistration registration;
    private int lockObj = 0;

    private DelayTask(TimeSpan delay, CancellationToken cancel)
    {
        timer = new Timer(OnElapsed, null, delay, Timeout.InfiniteTimeSpan);
        registration = cancel.Register(OnCancel);
    }

    public static Task Delay(TimeSpan delay, CancellationToken cancel) => new DelayTask(delay, cancel).tcs.Task;

    private void OnCancel() => SetResult(false);
    private void OnElapsed(object state) => SetResult(true);
    private void SetResult( bool completed)
    {
        if (Interlocked.Exchange(ref lockObj, 1) == 0)
        {
            tcs.SetResult(completed);
            timer.Dispose();
            registration.Dispose();
        }
    }
}
// Returns true if cancelled, false if not cancelled.

public static Task<bool> DelayWithoutCancellationException(int delayMilliseconds, CancellationToken cancellationToken)
{
    var tcs = new TaskCompletionSource<bool>();
    var ctr = default(CancellationTokenRegistration);

    Timer timer = null;

    timer = new Timer(_ =>
    {
        ctr.Dispose();
        timer.Dispose();
        tcs.TrySetResult(false);
    }, null, Timeout.Infinite, Timeout.Infinite);

    ctr = cancellationToken.Register(() =>
    {
        timer.Dispose();
        tcs.TrySetResult(true);
    });

    timer.Change(delayMilliseconds, Timeout.Infinite);

    return tcs.Task;
}
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace MultitargetedConsole
{
    class Program
    {
        static async Task Main()
        {
            await test();
        }

        static async Task test()
        {
            const int CANCEL1_TIMEOUT =  5000;
            const int CANCEL2_TIMEOUT =  2000;
            const int DELAY_TIMEOUT   = 10000;
            using var tokenSource1 = new CancellationTokenSource(CANCEL1_TIMEOUT);
            using var tokenSource2 = new CancellationTokenSource();
            using var compositeTokenSource = CancellationTokenSource.CreateLinkedTokenSource(tokenSource1.Token, tokenSource2.Token);

            var compositeToken = compositeTokenSource.Token;

            var _ = Task.Run(() => // Simulate something else cancelling tokenSource2 after 2s
            {
                Thread.Sleep(CANCEL2_TIMEOUT);
                tokenSource2.Cancel();
            });

            var sw = Stopwatch.StartNew();

            bool result = await DelayWithoutCancellationException(DELAY_TIMEOUT, compositeToken);
            Console.WriteLine($"Returned {result} after {sw.Elapsed}");
        }
    }
}
const int CANCEL1_TIMEOUT =  5000;
const int CANCEL2_TIMEOUT =  3000;
const int DELAY_TIMEOUT   =  2000;