11
votes

C # Témoin de la tâche

Je dois exécuter une opération de processus longue dans un fil et continuer en renvoyant le résultat à une fonction. Voici mon code:

   new ProductEventArgs() { E = new Exception("timeout") }; 


0 commentaires

5 Réponses :


1
votes

Vous pouvez exécuter une tâche Task.Delay (Timeout) CODE> Tâche en parallèle et vérifiez quelle tâche a été la tâche à terminer (Que () code> est très pratique dans ce cas ):

public void FetchProduct(TimeSpan timeout)
{
    var fetchTask = Task<ProductEventArgs>.Factory.StartNew(
        () =>
        {
            try
            {
                // long operation which return new ProductEventArgs with a list of product
            }
            catch(Exception e)
            {
                return new ProductEventArgs() { E = e };
            }
        });
    Task<ProductEventArgs> resultTask;
    if(timeout != Timeout.InfiniteTimeSpan)
    {
        var timeoutTask = Task.Delay(timeout);
        resultTask = Task.WhenAny(resultTask, timeoutTask).ContinueWith<ProductEventArgs>(
            t =>
            {
                // completed task is the result of WhenAny
                if(t.Result == fetchTask)
                {
                    return fetchTask.Result;
                }
                else
                {
                    return new ProductEventArgs() { E = new TimeoutException() };
                }
            });
    }
    else
    {
        resultTask = fetchTask;
    }
    resultTask.ContinueWith(x => handleResult(x.Result), TaskScheduler.FromCurrentSynchronizationContext());
}


0 commentaires

5
votes

Ce code fait ce que vous avez exprimé ici:

var timeout = TimeSpan.FromSeconds(5);

var actualTask = new Task<ProductEventArgs>(() =>
{
    var longRunningTask = new Task<ProductEventArgs>(() =>
    {
        try
        {
            Thread.Sleep(TimeSpan.FromSeconds(10)); // simulates the long running computation
            return new ProductEventArgs();
        }
        catch (Exception e)
        {
            return new ProductEventArgs() { E = e };
        }
    }, TaskCreationOptions.LongRunning);

    longRunningTask.Start();

    if (longRunningTask.Wait(timeout)) return longRunningTask.Result;

    return new ProductEventArgs() { E = new Exception("timed out") };
});

actualTask.Start();

actualTask.Wait();

Console.WriteLine("{0}", actualTask.Result.E); // handling E


1 commentaires

Je vous aime: - * Je suis un peu nouveau sur c # et plus sur la gestion du fil et vous faites ma journée!



18
votes

Vous devez utiliser CANCellationToken code> S:

var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
var token = cts.Token;
Task<ProductEventArgs>.Factory.StartNew(() =>
{
    try
    {
        // occasionally, execute this line:
        token.ThrowIfCancellationRequested();
    }
    catch (OperationCanceledException)
    {
        return new ProductEventArgs() { E = new Exception("timeout") };
    }
    catch (Exception e)
    {
        return new ProductEventArgs() { E = e };
    }

}).ContinueWith((x) => handleResult(x.Result), TaskScheduler.FromCurrentSynchronizationContext());


4 commentaires

Je suis complètement d'accord avec ça. Espérons que l'OP peut faire cela - j'ai répondu en fonction de l'hypothèse (peut-être éruption) selon laquelle l'OP appelait du code de son contrôle qui n'a pas soutenu des délais ou des annulations, donc j'ai supposé qu'il ne pouvait pas faire cela. J'espère qu'il peut. :)


Dans votre code, je dois juste donner le jeton comme une seconde ARG à la méthode de StartNew, non? Parce que tu n'as pas écrit. Si je l'ai fait et que j'ai eu un chômage de 0sec, par exemple, j'ai une exception globale sur le résultat de la poignée (X.Result). Merci


Vous ne devriez pas passer le CANCellationToken à StartNew dans votre cas. Si vous le faites (comme vous l'avez observé), un CANCellationToken annulera réellement la tâche avant qu'il ne commence. Mais vous ne voulez pas que la tâche soit annulée, ce n'est donc pas le comportement que vous voulez.


Votre code fonctionnera sur .NET 4.5 et plus, puisque sur .NET 4.0, CANCellationTOKENSource n'a que constructeur par défaut.



3
votes

Vous pouvez utiliser l'objet de tâche retourné pour la méthode StartNew, puis la méthode d'attente de l'utilisateur pour déterminer le délai d'attente.

Task<ProductEventArgs> task = Task<ProductEventArgs>.Factory.StartNew(() => {...});
if (!Task.Wait(new TimeSpan(0,0,1,0)) // wait for 1 minute
{
   // throw exception or something else if timeout
}


0 commentaires

0
votes

Démarrez une autre tâche dans la tâche principale (port de substitution):

Task.Factory.StartNew(() => 
        {
            // returns a string result
            var tsk = new Task<string>(() => { return VeryImportantThingsToDo(); });
            try
            {
                tsk.Start();
                if (!tsk.Wait(5000))
                    throw new TimeoutException();
                return tsk.Result;
            }
            catch (TimeoutException)
            {
                // Jabba Dabba Doooooooohhhhhh
            }

            return "<unknown>";
        }).ContinueWith((o) => string result = o.Result));


0 commentaires