2015-04-23 5 views
5

Ho un metodo asincrono dopo il completamento del quale desidero eseguire un altro metodo. Funziona bene se chiamo semplicemente il metodo e aggiungo .ContinueWith()Creare un'attività Cold Awaitable

Tuttavia, ho un nuovo requisito che è quello di avviare l'attività solo se sono in grado di aggiungerlo a un dizionario concorrente.

desidero costruire il compito, tentare di aggiungere e poi avviare l'attività

Tuttavia, sembra che Task.Start() completa immediatamente l'attività che causa l'azione continuerà a funzionare e le eventuali attese a .. non aspettare.

qualcuno può spiegare perché questo accade e il modo corretto per raggiungere il mio obiettivo?

namespace UnitTestProject2 
{ 
    [TestClass] 
    public class taskProblem 
    { 
     [TestMethod] 
     public void Test() 
     { 
      CancellationTokenSource cancel = new CancellationTokenSource(); 
      ConcurrentDictionary<Guid, Task> tasks = new ConcurrentDictionary<Guid,Task>(); 
      Guid id = Guid.NewGuid(); 
      Task t = new Task(async() => await Get(), cancel.Token); 
      t.ContinueWith(Complete); 
      if (tasks.TryAdd(id, t)) 
      { 
       t.Start(); 
      } 
      else 
      { 
       //another thread is stopping stuff dont start new tasks 
      } 

      t.Wait(); //expected to wait for the get function to complete 
      Console.WriteLine("end test"); 
     } 

     public async Task Get() 
     { 
      Console.WriteLine("start task"); 
      await Task.Delay(10000); 
      Console.WriteLine("end task"); 
     } 

     public void Complete(Task t) 
     { 
      Console.WriteLine("Complete"); 
     } 
    } 
} 

uscita:

start task 
end test 
Complete 

risultato atteso:

start task 
end task 
Complete 
end test 

Aggiornamento: Sembra non v'è alcun modo per creare una nuova attività, che non si avvia immediatamente o completa subito su Task.Start?

+1

Qual è il vero problema che stai cercando di risolvere?Il requisito non ha senso e il wrapping di Task a due livelli non aiuta. Non c'è * mai * un buon motivo per creare attività a freddo e chiamare 'Start'. Inoltre, non c'è motivo di racchiudere 'Get' che già restituisce un'attività in esecuzione in un altro. Basta scrivere 'Task t = Get();'. Per quanto riguarda il requisito, non ha senso. È sempre possibile aggiungere un nuovo elemento a un dizionario concorrente poiché in effetti si sta creando un nuovo codice Guida per esso. –

+1

Sembra che tu stia passando un delegato asincrono nel costruttore 'Task' che non attenderà il completamento dell'attività da 'Get'. Perché stai provando a racchiudere la chiamata in "Get" in un'altra attività? – Lee

+0

l'app reale invia notifiche push da una coda e ha un metodo Stop() (chiamato da un altro thread) per interrompere l'avvio di nuove attività. In questo caso ho bisogno del task non avviato in modo da poterlo aggiungere al dizionario simultaneo in modo thread-safe. ovviamente ci sono altri modi per farlo, che sarò costretto a usare se non riesco a capirlo. – Ewan

risposta

6

Il delegato è vuoto asincrono. i metodi void asincroni sono fuoco e dimenticano.

Vedi primo punto di sintesi di modelli e anti-pattern: http://rarcher.azurewebsites.net/Post/PostContent/31

Forse si può fare qualcosa di simile:

[TestFixture] 
public class FIXTURENAMETests { 
    [Test] 
    public async Task NAME() { 
    var tcs = new TaskCompletionSource<bool>(); 
    Task t = LongRunningStuff(tcs); 

    if (CanInsertInDictionary(t)) { 
     tcs.SetResult(true); 
    } else { 
     tcs.SetException(new Exception()); 
    } 

    Trace.WriteLine("waiting for end"); 

    try { 
     await t; 
    } 
    catch (Exception exception) { 
     Trace.WriteLine(exception); 
    } 

    Trace.WriteLine("end all"); 
    } 

    private bool CanInsertInDictionary(Task task) { 
    return true; 
    } 

    private async Task LongRunningStuff(TaskCompletionSource<bool> tcs) { 
    Trace.WriteLine("start"); 
    try { 
     await tcs.Task; 
    } 
    catch (Exception) { 
     return; 
    } 
    Trace.WriteLine("do long running stuff"); 
    await Task.Delay(10000); 
    Trace.WriteLine("end"); 
    } 
} 
+0

Ahh! mi sono reso conto che doveva trattarsi di qualcosa del anon delgate. ma hai anche la soluzione? – Ewan

+0

ho modificato la risposta. forse puoi fare qualcosa del genere. –

+0

Hmmm, un altro lavoro possibile, ma non c'è davvero alcun modo di creare un'attività non avviata e non avviata? davvero voglio fare Task t = Get(). DontStartYet() o qualcosa del genere – Ewan

-1

In primo luogo, ContinueWith restituirà un nuovo Task, si vuole aspettare fino a il metodo Complete è completo ma è in attesa della prima attività t.

Quindi, all'uscita Complete prima end test, bisogna aspettare il secondo compito:

Task t = new Task(async() => await Get(), cancel.Token); 

// NOTE: t2 is a new Task returned from ContinueWith 
Task t2 = t.ContinueWith(Complete); 

if (tasks.TryAdd(id, t)) 
{ 
    t.Start(); 
} 
else 
{ 
} 

// NOTE: Waiting on t2, NOT t 
t2.Wait(); 

Console.WriteLine("end test"); 

Ora l'uscita sarà:

start task 
Complete 
end test 
end task 

Ok, questo non è ancora il risultato previsto . end task deve essere stampato prima dello Complete. Questo perché l'azione asincrona non è attendibile: How to await on async delegate

Non so se ho compreso correttamente le tue esigenze. Se sono io, potrei fare in questo modo:

aggiungere una nuova classe di supporto:

public class TaskEntry 
{ 
    public Task Task { get; set; } 
} 

quindi modificare il codice per:

Guid id = Guid.NewGuid(); 

Task task = null; 
var entry = new TaskEntry(); 
if (tasks.TryAdd(id, entry)) 
{ 
    entry.Task = Get(); 

    // Notice this line of code: 
    task = entry.Task.ContinueWith(Complete); 
} 

if (task != null) 
{ 
    task.Wait(); 
} 

Console.WriteLine("end test"); 

Qui Suppongo che il TaskEntry non ci sarà modificato da altri thread.

+0

Sei corretto Probabilmente dovrei attendere l'attività di continuazione per garantire il risultato atteso. ma il vero problema è il delegato del vuoto asincrono. La tua classe TaskEntry aggiuntiva è un buon lavoro. Ma stai dicendo che non c'è modo di creare un'attività attendibile? – Ewan