| 

.NET C# Java Javascript Exception

3
Hallo,

bisher habe ich nur Beispiele gefunden, die innerhalb einer asynchronen Methode eine oder mehrere weitere asynchrone Methoden aufrufen. Wie erstellt man aber eine asynchrone Methode, die auf bestehenden nicht asynchronen Code zurückgreift. (Best Practice).

private async Task<IEnumerable<User>> GetUsersAsync(CancellationToken cancellationToken)
{
// hier synchroner Zugriff auf eine DataSource
}

Wie würde hier der Methodenbody aussehen?

Danke im Voraus.
News:
16.07.2013
lbm1305 849 1 8
5 Antworten
2
Wenn ich Frage und Kommentar verbinde, würde Deine derzeitige Lösung so aussehen:

private async Task<IEnumerable<User>> GetUsersAsync(CancellationToken cancellationToken)
{
return await Task.Run<IEnumerable<User>>(()=> DataSource.GetUser());
}

Die Schlüsselwörter async/await kannst Du rauskürzen, Du brauchst Sie in diesem Fall nicht, du kannst die neue Task direkt zurückgeben. Ein await der GetUsersAsync-Methode funktioniert weiterhin, weil der Rückgabetyp Task<T> ist.

Auf den ersten Blick scheint das Problem damit gelöst. Leider nur auf den ersten Blick. Wenn Du die Methode mehrfach hintereinander aufrufst und die vorher gestarteten Tasks noch nicht vollständig abgearbeitet wurden, wirst Du mit mehreren Threads "gleichzeitig" auf die Shared Resource zugreifen - und das ist ja das, was Du vermeiden musst. Du musst die Aufrufe synchronisieren, um sie linear nacheinander abzuarbeiten. Spontan würde ich sagen, dass man sowas auch wiederverwendbar in einer eigenen Klasse unterbringen kann. Spontan aus der Hüfte etwa so:
public class AsyncSerializer<T>
{
private readonly object _syncRoot = new object();

public Task<T> SerializeCall(Func<T> syncCall)
{
return Task.Run(() =>
{
lock (_syncRoot)
return syncCall();
});
}
}

In Deiner Klasse erstellst Du Dir dann pro SharedResource eine Instanz des AsyncSerializer und verwendest diesen, um die eigentlich synchronen Aufrufe zu linearisieren während Du sie gleichzeitg mithilfe von Tasks in nebenläufige Aktionen transformierst.
Man müsste noch drüber nachdenken, ob man bei der Taskerstellung evtl. die LongRunning Option setzen sollte. Kommt dann auf den Anwendungsfall an.

Wenn Du die SharedResource an mehreren unabh. Stellen im Code verwendest, bräuchtest Du einen gemeinsam genutzten AsyncSerializer. Das ist sicher nicht befriedigend, weshalb ich Dir dann dazu raten würde,

1. für die SharedResource ein Interface einzuziehen (falls nicht schon vorhanden),
2. einen Decorator zu implementieren, der die Aufrufe mit Hilfe von AsyncSerializer linearisiert und
3. die Decorator-Implenentierung statt der bisherigen in der Applikation zu nutzen.

Bei konsequenter DI und mit einem halbwegs ordentlichen IoC Container kannst Du das alles im Bootstrapping erledigen.

Länger geworden als ich dachte :-) hope this helps.
16.07.2013
ffordermaier 8,4k 3 9
1
Kommt ganz darauf an was du machen willst. Im Idealfall ein Lock auf die synchronenen Objekte um die Daten zu bekommen, Lock lösen und dann die Zeitaufwendigen Berechnungen machen.
Wenn du aber nur versuchst die Daten async zu bekommen und diene Datenquelle das aber nicht unterstützt, dann wird es nicht gehen.
16.07.2013
Floyd 14,6k 3 9
1
Deine asynchrone Methode kapselt dann den Start eines neuen Threads, der den (bestehenden) synchronen Code ausführt ...
Ob nun Threadpool, neuer Thread, ... musst Du schauen, was Du benötigst.
16.07.2013
Xantiva 2,3k 2 9
Das ist auch mein Gedanke, dass ich nochmals einen Task starte, der dann den Call durchführt und dann das Ergebnis weiterreicht. Die Frage ist, ob es so korrekt ist oder eher schlechter Stil :-/

return await Task.Run<IEnumerable<User>>(()=> DataSource.GetUser());
lbm1305 16.07.2013
1
Schau Dir auch mal http://stackoverflow.com/questions/11674073/c-sharp-have-async-function-call-synchronous-function-or-synchronous-function-ca an und überlege, ob Du den asynchronen Wrapper um GetUsers wirklich brauchst.
16.07.2013
Matthias Hlawatsch 13,2k 4 9
0
Hallo,

danke für die Antworten, die für die Arbeit mit async / await sehr von Vorteil sind bzw. sein können. Mein Anliegen war aber sicherlich falsch formuliert. Mir ging es hauptsächlich darum, wie man einen asynchrone Methode erstellt, die bspw. eine lang andauernde Operation kapselt, die eine eigene Berechnung sein kann.
Wie sieht hier der optimale Weg aus? Wie wird das CancellationToken korrekt behandelt?

Könnste so eine Metode aussehen?
public Task<Stream> OpenAsync(FileAccess fileAccess)
{
Stream ret;
if (fileAccess == FileAccess.Read)
{
ret = File.OpenRead(Path);
}
else if (fileAccess == FileAccess.ReadAndWrite)
{
ret = File.Open(Path, FileMode.Open, System.IO.FileAccess.ReadWrite);
}
else
{
throw new ArgumentException("Unrecognized FileAccess value: " + fileAccess);
}

return Task.FromResult(ret);
}

Quelle: http://pclstorage.codeplex.com/SourceControl/latest#src/PCLStorage.FileSystem.Desktop/FileSystemFile.cs

Eine andere Möglichkeit wäre ja bspw. innerhalb der Methode die Operation in einem Task.Run zu kapseln. Hier habe ich aber auch schon gelesen, dass dies in Librarycode vermieden werden soll.
17.07.2013
lbm1305 849 1 8

Stelle deine .net-Frage jetzt!
TOP TECHNOLOGIES CONSULTING GmbH