如何在C#4.0中使任务进入睡眠状态(或延迟)?


71

Task.Delay.NET 4.5中

在.NET 4.0中我该如何做?



在这里看看:msdn.microsoft.com/en-us/library/hh194845.aspx或使用Thread.Sleep并添加对使用System.Threading的引用;
马克斯


4
@Default,对于使用Sleep()而言,任务应该始终:1)生成单独的线程,以及2)仅一个。3)它不能重复使用。这对于任务没有任何意义。这不是骗人的,您的链接是有关延迟任务开始的。我的问题是,在开始
播放

11
使用Thread.Sleep在你的代码几乎总是一个错误。
Eli Arbel 2013年

Answers:


60

您可以使用在4.0中Timer创建Delay方法:

public static Task Delay(double milliseconds)
{
    var tcs = new TaskCompletionSource<bool>();
    System.Timers.Timer timer = new System.Timers.Timer();
    timer.Elapsed+=(obj, args) =>
    {
        tcs.TrySetResult(true);
    };
    timer.Interval = milliseconds;
    timer.AutoReset = false;
    timer.Start();
    return tcs.Task;
}

谢谢,我没有指定,但是我想在我的WPF应用程序中添加多线程。我应该在此处使用带有回调参数的Timer吗?并在Callback()定义上使用Dispatcher.BeginInvoke()
可靠

@Fulproof计时器触发时,您不会执行任何UI交互,因此没有理由。
Servy 2013年

1
如何向它添加cancelToken,Task.Delay提供哪个?
Imran Rizvi

2
这不是创造竞争条件吗?如果计时器对象在计时器到期之前碰巧得到了垃圾回收,似乎TrySetResult永远不会被调用。
爱德华·布雷

10
@EdwardBreyTimer该类专门在内部进行处理,以确保其用户在其生命周期内无需保留对其的引用。只要计时器当前正在运行,它就会从有根的位置添加对自身的引用,然后在不再运行时将其删除。
Servy 2013年


26
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        Delay(2000).ContinueWith(_ => Console.WriteLine("Done"));
        Console.Read();
    }

    static Task Delay(int milliseconds)
    {
        var tcs = new TaskCompletionSource<object>();
        new Timer(_ => tcs.SetResult(null)).Change(milliseconds, -1);
        return tcs.Task;
    }
}

在“如何在4.0中实现Task.Delay ”部分


3
我将示例添加到您的答案中,以防链接由于某种原因而失效。
默认值

2
@Fulproof他写的是使用LinqPad,它添加了一个扩展方法,可以将方法object的值打印ToString出来。请注意,他没有在实际实现中使用该方法,也没有使用任何其他非库方法,而只是对其进行测试的示例函数。
Servy 2013年

@Servy,谢谢。我问这个问题是为了减少未知数(并获得答案),但又不增加难题来解决。即,一个问的人通常没有专门知识来完成谜题
可靠的2013年

1
已更新,因此您只需复制粘贴即可运行)
QrystaL 2013年

不应处置计时器吗?
阿米特G

6

以下是可取消Task.Delay实现的代码和示例工具。您可能对该Delay方法感兴趣:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DelayImplementation
{
    class Program
    {
        static void Main(string[] args)
        {
            System.Threading.CancellationTokenSource tcs = new System.Threading.CancellationTokenSource();

            int id = 1;
            Console.WriteLine(string.Format("Starting new delay task {0}. This one will be cancelled.", id));
            Task delayTask = Delay(8000, tcs.Token);
            HandleTask(delayTask, id);

            System.Threading.Thread.Sleep(2000);
            tcs.Cancel();

            id = 2;
            System.Threading.CancellationTokenSource tcs2 = new System.Threading.CancellationTokenSource();
            Console.WriteLine(string.Format("Starting delay task {0}. This one will NOT be cancelled.", id));
            var delayTask2 = Delay(4000, tcs2.Token);
            HandleTask(delayTask2, id);

            System.Console.ReadLine();
        }

        private static void HandleTask(Task delayTask, int id)
        {
            delayTask.ContinueWith(p => Console.WriteLine(string.Format("Task {0} was cancelled.", id)), TaskContinuationOptions.OnlyOnCanceled);
            delayTask.ContinueWith(p => Console.WriteLine(string.Format("Task {0} was completed.", id)), TaskContinuationOptions.OnlyOnRanToCompletion);
        }

        static Task Delay(int delayTime, System.Threading.CancellationToken token)
        {
            TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();

            if (delayTime < 0) throw new ArgumentOutOfRangeException("Delay time cannot be under 0");

            System.Threading.Timer timer = null;
            timer = new System.Threading.Timer(p =>
            {
                timer.Dispose(); //stop the timer
                tcs.TrySetResult(null); //timer expired, attempt to move task to the completed state.
            }, null, delayTime, System.Threading.Timeout.Infinite);

            token.Register(() =>
                {
                    timer.Dispose(); //stop the timer
                    tcs.TrySetCanceled(); //attempt to mode task to canceled state
                });

            return tcs.Task;
        }
    }
}

看起来不好!如果使用相同的CancellationToken调用很多延迟,则将在令牌上注册很多代表。
RollingStone



0

在许多情况下,纯粹的AutoResetEvent优于Thread.Sleep()...

AutoResetEvent pause = new AutoResetEvent(false);
Task timeout = Task.Factory.StartNew(()=>{
pause.WaitOne(1000, true);
});

希望对你有帮助


我认为最初的想法是阻塞当前线程,而不是等待另一个线程,而只是稍后在当前线程上得到通知。那么,呢?
亚历克斯R.17年

0
    public static void DelayExecute(double delay, Action actionToExecute)
    {
        if (actionToExecute != null)
        {
            var timer = new DispatcherTimer
            {
                Interval = TimeSpan.FromMilliseconds(delay)
            };
            timer.Tick += (delegate
            {
                timer.Stop();
                actionToExecute();
            });
            timer.Start();
        }
    }

0

这是一个基于计时器的简洁实现,并进行了适当的清理:

var wait = new TaskCompletionSource<bool>();
using (new Timer(_ => wait.SetResult(false), null, delay, Timeout.Infinite))
    await wait.Task;

要在.NET 4.0上使用此代码,您需要Microsoft.Bcl.Async NuGet程序包。


OP正在询问4.0,4.0中没有“ await”。
亚历克斯R.17年

await关键字是C#语言,这是从.NET Framework版本分开的一部分。问题是,Task<T>没有GetAwaiter,这await依赖。Microsoft.Bcl.Async提供了这一点。我更新了答案以提及NuGet软件包。
爱德华·布雷

1
如果使用Microsoft.Bcl.Async,不是TaskEx.Delay更简洁吗?
亚历克斯R.17年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.