6

ich ein Klasse-Receiver mit einem ActionBlock habe:erwarten 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); 
    } 

    //... 
} 

Die Register-Aktion für den ActionBlock ist eine Asynchron-Methode mit einem await-Statement:

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

Jetzt was ich tun möchte, ist, synchron zu warten (wenn eine Bedingung eingestellt ist), bis die Aktionsmethode beendet ist, um ein exklusives Verhalten zu erhalten:

Das Problem ist, wenn die "warten auf Task.Delay (500);" Anweisung ausgeführt wird, die "receiver.Post (5) .Wait();" wartet nicht mehr.

Ich habe mehrere Varianten (TaskCompletionSource, ContinueWith, ...) versucht, aber es funktioniert nicht.

Hat jemand eine Idee, wie man das Problem löst?

+0

Couldn‘ t Sie ändern Ihren Code, indem Sie '_receiver' in einen' TransformBlock' ändern und die folgende Aktion in einen neuen 'ActionBlock' einfügen, der mit' _receiver' verknüpft ist? – svick

+0

Können Sie mir ein kleines Codebeispiel geben? Ich verstehe nicht, wie dieses Refactoring durch "exklusives Verhalten" lösen sollte. – obi111

Antwort

3

ActionBlock wird standardmäßig exklusives Verhalten erzwingen (es wird jeweils nur ein Element verarbeitet). Wenn Sie etwas anderes von „exklusiven Verhalten“ bedeuten, können Sie TaskCompletionSource verwenden Ihren Absender zu benachrichtigen, wenn die Aktion abgeschlossen ist:

... 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); 
} 

Alternativ können Sie verwenden 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

Ja, Sie haben Recht, dass der ActionBlock exklusives Verhalten erzwingt, aber wenn die registrierten Aktionen asynchron sind, ist es nicht mehr "echt exklusiv". Ja, Ihre Lösung sollte funktionieren, aber ich möchte keinen TaskCompletionSource-Parameter hinzufügen, da diese Aktion der Einstiegspunkt für die exklusive Logik ist - wenn also ein Benutzer tcs.SetResult nicht aufruft, funktioniert es nicht mehr ... – obi111

+0

In diesem Fall Fall, könnten Sie AsyncLock verwenden. Siehe aktualisierte Antwort für ein Codebeispiel. Sie wissen nicht mehr, wann ein Artikel verarbeitet wurde, aber jeder Artikel wird einzeln verarbeitet (einschließlich "Async" -Verarbeitung). –

+0

ok danke ich denke, das ist genau das, was ich brauche - ich werde es versuchen! – obi111