6

Ho un ricevitore classe con un'ActionBlock:attendere async lambda in ActionBlock

public class Receiver<T> : IReceiver<T> 
{ 

    private ActionBlock<T> _receiver; 

    public Task<bool> Send(T item) 
    { 
    if(_receiver!=null) 
     return _receiver.SendAsync(item); 

    //Do some other stuff her 
    } 

    public void Register (Func<T, Task> receiver) 
    { 
    _receiver = new ActionBlock<T> (receiver); 
    } 

    //... 
} 

Il registro-azione per l'ActionBlock è un async-metodo con un await-Statement:

private static async Task Writer(int num) 
{ 
    Console.WriteLine("start " + num); 
    await Task.Delay(500); 
    Console.WriteLine("end " + num); 
} 

Ora quello che voglio fare è attendere sincrono (se una condizione è impostata) fino a quando il metodo azione è finito per ottenere un comportamento esclusivo:

var receiver = new Receiver<int>(); 
receiver.Register((Func<int, Task) Writer); 
receiver.Send(5).Wait(); //does not wait the action-await here! 

Il problema è quando "attendi Task.Delay (500);" l'istruzione è eseguita, il "receiver.Post (5) .Wait();" non aspetta più

Ho provato diverse varianti (TaskCompletionSource, ContinueWith, ...) ma non funziona.

Qualcuno ha un'idea di come risolvere il problema?

+0

Couldn' Se cambi il tuo codice cambiando '_receiver' in un' TransformBlock' e metti la seguente azione in un nuovo 'ActionBlock', collegato a' _receiver'? – svick

+0

Puoi darmi un piccolo esempio di codice? Non capisco come quel refactoring debba risolvere con il problema del "comportamento esclusivo". – obi111

risposta

3

ActionBlock per impostazione predefinita applica il comportamento esclusivo (viene elaborato un solo elemento alla volta). Se si intende qualcos'altro da un "comportamento esclusivo", è possibile utilizzare TaskCompletionSource per informare il mittente quando l'azione è completa:

... use ActionBlock<Tuple<int, TaskCompletionSource<object>>> and Receiver<Tuple<int, TaskCompletionSource<object>>> 
var receiver = new Receiver<Tuple<int, TaskCompletionSource<object>>>(); 
receiver.Register((Func<Tuple<int, TaskCompletionSource<object>>, Task) Writer); 
var tcs = new TaskCompletionSource<object>(); 
receiver.Send(Tuple.Create(5, tcs)); 
tcs.Task.Wait(); // if you must 

private static async Task Writer(int num, TaskCompletionSource<object> tcs) 
{ 
    Console.WriteLine("start " + num); 
    await Task.Delay(500); 
    Console.WriteLine("end " + num); 
    tcs.SetResult(null); 
} 

In alternativa, è possibile utilizzare AsyncLock (included in my AsyncEx library):

private static AsyncLock mutex = new AsyncLock(); 

private static async Task Writer(int num) 
{ 
    using (await mutex.LockAsync()) 
    { 
    Console.WriteLine("start " + num); 
    await Task.Delay(500); 
    Console.WriteLine("end " + num); 
    } 
} 
+0

sì, hai ragione che ActionBlock applica un comportamento esclusivo, ma se le azioni registrate sono asincrone non è più "reale". sì la soluzione dovrebbe funzionare ma non voglio aggiungere un parametro TaskCompletionSource perché quella azione è il punto di ingresso per la logica esclusiva - quindi se un utente non chiama tcs.SetResult non funziona più ... – obi111

+0

In quello caso, è possibile utilizzare 'AsyncLock'. Vedi la risposta aggiornata per un esempio di codice. Non si sa più quando un oggetto viene elaborato, ma ogni oggetto verrà elaborato uno alla volta (inclusa l'elaborazione 'async'). –

+0

ok grazie penso che sia esattamente quello di cui ho bisogno - lo proverò! – obi111