Wie kann eine Aufgabe in C # 4.0 in den Ruhezustand versetzt (oder verzögert) werden?

71

In .NET 4.5 gibt es Task.Delay

Wie kann ich dasselbe in .NET 4.0 tun?

Fulproof
quelle
2
Thread.Sleep ?
Standard
Schauen Sie hier nach: msdn.microsoft.com/en-us/library/hh194845.aspx Oder verwenden Sie Thread.Sleep und fügen Sie einen Verweis auf die Verwendung von System.Threading hinzu.
Max
Mögliches Duplikat: stackoverflow.com/q/4990602
Standard
4
@Default, für die Verwendung von Sleep () sollte die Aufgabe immer: 1) einen separaten Thread erzeugt haben und 2) nur einen. Nicht weniger und nicht mehr und 3) es kann nicht wiederverwendet werden. Nichts davon gilt für eine Aufgabe. Es ist kein Betrug, bei Ihrem Link geht es darum, den Start einer Aufgabe zu verzögern. Meine Frage ist, es jeden Moment nach seinem Start in den Schlaf zu versetzen
Fulproof
11
Die Verwendung Thread.Sleepin Ihrem Code ist fast immer ein Fehler.
Eli Arbel

Antworten:

60

Mit a können Sie Timereine DelayMethode in 4.0 erstellen :

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;
}
Servieren
quelle
Vielen Dank, ich habe nicht angegeben, aber ich versuche, meiner WPF-Anwendung Multithreading hinzuzufügen. Soll ich dort das Argument Timer mit Rückruf verwenden? Und in der Callback()Definition verwenden Dispatcher.BeginInvoke()?
Fulproof
@Fulproof Sie führen keine UI-Interaktion durch, wenn der Timer ausgelöst wird. Es gibt also keinen Grund dafür.
Servy
1
Wie füge ich CancellationToken hinzu, das Task.Delay bereitstellt?
Imran Rizvi
2
Schafft dies nicht eine Rennbedingung? Wenn das Timer-Objekt vor Ablauf des Timers Müll sammelt, wird dieser anscheinend TrySetResultnie aufgerufen.
Edward Brey
10
@EdwardBrey Die TimerKlasse behandelt dies speziell intern, um sicherzustellen, dass Benutzer nicht während ihrer gesamten Lebensdauer an einem Verweis darauf festhalten müssen. Solange der Timer gerade ausgeführt wird, fügt er an einem verwurzelten Speicherort einen Verweis auf sich selbst hinzu und entfernt ihn dann, wenn er nicht mehr ausgeführt wird.
Servy
75

Verwenden Sie das Microsoft.Bcl.Async- Paket von NuGet TaskEx.Delay.

Eli Arbel
quelle
1
Wichtige Information ist, dass der Klassenname TaskEx und nicht Task ist !
OneWorld
Dies sollte eine Antwort sein.
Moien
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;
    }
}

Aus dem Abschnitt Implementieren von Task.Delay in 4.0

QrystaL
quelle
3
Ich habe das Beispiel zu Ihrer Antwort hinzugefügt, falls der Link aus irgendeinem Grund nicht mehr funktioniert.
Standard
2
@Fulproof Er schrieb, dass er LinqPad verwendet, das eine Erweiterungsmethode hinzufügt, die objectden Wert seiner ToStringMethode ausgibt . Beachten Sie, dass er dies und keine anderen Methoden außerhalb der Bibliothek in seiner tatsächlichen Implementierung verwendet, sondern nur die Beispielfunktion, mit der es getestet wird.
Servy
@Servy, danke. Ich habe die Frage gestellt, um die Anzahl der Unbekannten zu verringern (und Antworten zu erhalten), aber keine Rätsel zum Lösen hinzuzufügen. Dh eine Person, die fragt, hat normalerweise kein Fachwissen, um das Rätsel zu
lösen
1
Aktualisiert, so dass Sie einfach kopieren, einfügen und ausführen können)
QrystaL
Sollte der Timer nicht entsorgt werden?
Amit G
6

Unten finden Sie den Code und das Beispielkabel für eine stornierbare Task.Delay-Implementierung. Sie interessieren sich wahrscheinlich für die DelayMethode:

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;
        }
    }
}
Gusdor
quelle
Sieht nicht gut aus! Wenn ich mit demselben CancellationToken viel Verspätung aufrufe, werden viele Delegierte auf dem Token registriert.
RollingStone
1

Erweiterung der Idee aus dieser Antwort :

new AutoResetEvent(false).WaitOne(1000);
Alex R.
quelle
0

Sie können das Visual Studio Async CTP herunterladen und TaskEx.Delay verwenden

DVD
quelle
5
Das ist keine gute Idee, das CTP enthält bekannte Fehler, die niemals behoben werden. Die Verwendung von Bcl.Async ist eine viel bessere Wahl.
Svick
0

In vielen Fällen ist ein reines AutoResetEvent besser als ein Thread.Sleep () ...

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

hoffe, dass es hilft

f.capet
quelle
Ich denke, die ursprüngliche Idee ist, den aktuellen Thread zu blockieren, nicht auf einen weiteren Thread zu warten und erst später über den aktuellen Thread benachrichtigt zu werden. Wie wäre es damit ?
Alex R.
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();
        }
    }
WndRnr
quelle
0

Hier ist eine prägnante, zeitgesteuerte Implementierung mit ordnungsgemäßer Bereinigung:

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

Um diesen Code unter .NET 4.0 verwenden zu können, benötigen Sie das Microsoft.Bcl.Async NuGet-Paket.

Edward Brey
quelle
Das OP fragt nach 4.0 - in 4.0 gibt es kein "Warten".
Alex R.
Das awaitSchlüsselwort ist Teil der C # -Sprache, die von der .NET Framework-Version getrennt ist. Das Problem ist, dass Task<T>es nicht gibt GetAwaiter, worauf es awaitankommt. Microsoft.Bcl.Async liefert dies. Ich habe meine Antwort aktualisiert, um das NuGet-Paket zu erwähnen.
Edward Brey
1
Wenn man Microsoft.Bcl.Async verwendet , ist es dann nicht TaskEx.Delayprägnanter?
Alex R.