6

motivazione

C# 5.0 asincrona/attendono costrutti sono impressionanti, purtroppo ancora Microsoft visualizzati solo una release candidate di entrambi .NET 4.5 e VS 2012, e ci vorrà un po 'di tempo prima che queste tecnologie vengano ampiamente adottate nei nostri progetti.Implementazione timeout asincrono utilizzando poveri asincrona mans/attendono costrutti in .Net 4.0

In Stephen Toub Asynchronous methods, C# iterators, and Tasks ho trovato un sostituto che può essere ben utilizzato in. NET 4.0. Ci sono anche una dozzina di altre implementazioni che rendono possibile utilizzando l'approccio anche in .NET 2.0 se sembrano po 'obsolete e meno ricco di funzionalità.

Esempio

Così ora il mio codice .NET 4.0 si presenta come (sezioni commentate mostrano come si è fatto in .NET 4.5):

//private async Task ProcessMessageAsync() 
private IEnumerable<Task> ProcessMessageAsync() 
{ 
    //var udpReceiveResult = await udpClient.ReceiveAsync(); 

    var task = Task<UdpAsyncReceiveResult> 
       .Factory 
       .FromAsync(udpClient.BeginReceive, udpClient.EndReceive, null); 

    yield return task; 

    var udpReceiveResult = task.Result; 

    //... blah blah blah 

    if (message is BootstrapRequest) 
    { 
     var typedMessage = ((BootstrapRequest)(message)); 

     // !!! .NET 4.0 has no overload for CancellationTokenSource that 
     // !!! takes timeout parameter :(
     var cts 
      = new CancellationTokenSource(BootstrapResponseTimeout); // Error here 

     //... blah blah blah 

     // Say(messageIPEndPoint, responseMessage, cts.Token); 

     Task.Factory.Iterate(Say(messageIPEndPoint, responseMessage, cts.Token)); 
    } 
} 

Sembra po 'brutto se si fa il lavoro

La domanda

Quando si utilizza CancellationTokenSource NET 4.5 c'è un costruttore che prende periodo come parametro timeout, cosicché risulta CancellationTokenSource annulla entro un periodo di tempo specificato.
.Net 4.0 non è in grado di timeout, quindi qual è il modo corretto di farlo in .Net 4.0?

+0

questione connessa: http://stackoverflow.com/questions/9110472/using-async-await-on- net-4 – CodesInChaos

+0

Changed "timer" a "timeout" nel titolo in quanto la questione è di circa timeout e "timer" (utilizzato per la ricorrente con alcune esecuzioni d'epoca) è stato fuorviante e disorientante –

risposta

4

Questo ha davvero nulla a che fare con async/attendere? Sembra che si sta solo bisogno di un modo per annullare il token, indipendentemente async/attendono, giusto? In tal caso, potresti semplicemente creare un timer che chiama Annulla dopo il timeout?

new Timer(state => cts.Cancel(), null, BootstrapResponseTimeout, Timeout.Infinite); 

EDIT

La mia risposta iniziale di cui sopra è l'idea di base, ma una soluzione più robusta si può essere trovato in Is CancellationTokenSource.CancelAfter() leaky? (in realtà l'attuazione .Net 4.5 del costruttore che stai cercando). Ecco una funzione che puoi utilizzare per creare i token di timeout basati su quel codice.

public static CancellationTokenSource CreateTimeoutToken(int dueTime) { 
    if (dueTime < -1) { 
     throw new ArgumentOutOfRangeException("dueTime"); 
    } 
    var source = new CancellationTokenSource(); 
    var timer = new Timer(self => { 
     ((Timer)self).Dispose(); 
     try { 
      source.Cancel(); 
     } catch (ObjectDisposedException) {} 
    }); 
    timer.Change(dueTime, -1); 
    return source; 
} 
+0

Che tipo di timer che si propongono di usare Dax? – Lu4

+0

'System.Threading.Timer' è quello che pensavo, ma mi immagino nessuno di loro dovrebbe funzionare - io non sono un esperto di differenze tra i vari tipi di .Net' Timer's. –

+0

Ma bloccherà il thread e inizieremo a perdere tutti i vantaggi dell'approccio asincrono ... :( – Lu4

5

FWIW, è possibile utilizzare async/attendere nei progetti 4.0, è sufficiente utilizzare the async targeting pack. Funziona alla grande per me!

+1

Sicuramente risposta utile – Lu4

0

È comunque possibile utilizzare CancelAfter(), che è un metodo di estensione in Microsoft.Bcl.Async che è molto simile alla risposta accettata sopra.

Questo è il codice refereance souce quando si preme F12 per vedere la realizzazione di CancelAfter():

/// <summary>Cancels the <see cref="T:System.Threading.CancellationTokenSource" /> after the specified duration.</summary> 
    /// <param name="source">The CancellationTokenSource.</param> 
    /// <param name="dueTime">The due time in milliseconds for the source to be canceled.</param> 
    public static void CancelAfter(this CancellationTokenSource source, int dueTime) 
    { 
    if (source == null) 
     throw new NullReferenceException(); 
    if (dueTime < -1) 
     throw new ArgumentOutOfRangeException("dueTime"); 
    Contract.EndContractBlock(); 
    Timer timer = (Timer) null; 
    timer = new Timer((TimerCallback) (state => 
    { 
     timer.Dispose(); 
     TimerManager.Remove(timer); 
     try 
     { 
     source.Cancel(); 
     } 
     catch (ObjectDisposedException ex) 
     { 
     } 
    }), (object) null, -1, -1); 
    TimerManager.Add(timer); 
    timer.Change(dueTime, -1); 
    }