2016-01-20 15 views
5

Ich habe nur für einen groben Entwurf eine ganze Reihe von Datenzugriffsmethoden mit dem folgenden Muster in Async umgewandelt, und es sieht zu einfach aus, um für spätere Iterationen gut genug zu sein. Wie sicher ist es, was fehlt und wie soll ich es machen?Ist das Ausführen lang andauernder Anrufe asynchron so einfach?

Der Dienst, sorgt für lang laufende Anrufe:

public async Task<IdentityUser> GetByIdAsync(int id) 
{ 
    await Task.Run(() => _userService.GetById(id)); 
} 
+0

Lange laufende Abfragen haben eine lange Liste möglicher Probleme, mit denen Sie umgehen müssen. Warum nicht einfach das SignalR nugget installieren und alles für dich reparieren lassen - und auch den Websocket-Support! –

+0

@ MattiasÅslund: Unterschiedliche Leute haben unterschiedliche Definitionen von "long-running". Ich habe das Gefühl, GetById() ist nicht die Art von "long-running", die SignalR nutzen würde. – StriplingWarrior

+0

Ist es etwas, das Sie als WCF-Dienst oder als Web-API verwenden? Wenn das der Fall ist, verschärfen Sie nur die Server-Seite. – Noseratio

Antwort

5

Sie sollten nicht machen ":

private class UserService 
{ 
    public IdentityUser GetById(int id) 
    { 
     ... 
    } 
} 

private UserService _userService = new UserService(); 

Die ursprüngliche synchrone Methode:

public IdentityUser GetById(int id) 
{ 
    return _userService.GetById(id); 
} 

Meine fantastische neue Asynchron-Methode fake async "Methoden wie folgt:

public async Task<IdentityUser> GetByIdAsync(int id) 
{ 
    await Task.Run(() => _userService.GetById(id)); 
} 

Der Grund, warum ich es "Fake Async" nenne, ist, weil nichts wirklich async über die Operation ist. In diesem Fall sollten Sie nur die synchrone Methode verwenden. Wenn ein Anrufer es mithilfe von Task.Run asynchron machen möchte, kann er dies tun.

Wann ist etwas asynchron? Wenn Sie eine Anfrage an einen Webdienst oder eine Datenbank stellen, beispielsweise zwischen dem Senden der Anforderung und dem Empfangen der Antwort, gibt es eine Wartezeit - die Anforderung ist eine asynchrone Operation. Um das Aufrufen des aufrufenden Threads zu verhindern, verwenden Sie async-await.

+0

Ich muss die Methode implementieren genau wie 'public async Aufgabe GetByIdAsync (int id)', die nicht kompiliert wird, wenn ich ihre innere Operation nicht zu einer gefälschten Async mache, und 'UserService' bietet keine asynchronen Methoden an. – ProfK

+2

@ProfK, dann implementieren Sie es als 'Task.FromResult' zurückgeben und ohne' async' Schlüsselwort (das ist sowieso kein Teil der kompilierten Methode Unterschrift). – Noseratio

+1

@Noseratio Ich bin nett, wenn ich 'Task.FromResult' mag, und habe es an vielen Stellen benutzt, musste ich eine' Aufgabe' zurückgeben oder sogar 'eine' erwarten. Ich denke, ich könnte auf Nummer Sicher gehen und all meine 'Fälschungen' dazu benutzen. – ProfK

2

Technisch gesehen, die funktioniert, aber es funktioniert durch einen neuen Thread Schaffung eines synchronen Operation auszuführen, die selbst wickelt und die Blockierung auf einem in sich asynchronen Betrieb. Das bedeutet, dass Sie nicht die größten Vorteile bekommen, überhaupt zu gehen.

Der richtige Weg ist, asynchron den ganzen Weg zu gehen. Während jetzt haben Sie wahrscheinlich so etwas wie folgt aus:

private class UserService 
{ 
    public IdentityUser GetById(int id) 
    { 
     return mContext.Users.Single(u => u.Id == id); 
    } 
} 

... Sie sollten nun einen Asynchron-Version erstellen:

private class UserService 
{ 
    public async Task<IdentityUser> GetByIdAsync(int id) 
    { 
     return await mContext.Users.SingleAsync(u => u.Id == id); 
    } 
} 

Verbrauch:

public async Task<IdentityUser> GetByIdAsync(int id) 
{ 
    return await _userService.GetByIdAsync(id); 
} 

Gesetzt, natürlich, dass Ihr zugrundeliegendes Framework unterstützt asynchrone Methoden wie SingleAsync() für inhärent asynchrone Operationen. Dadurch kann das System den aktuellen Thread freigeben, während Sie auf die Datenbanken warten e Operation zum Abschluss. Der Thread kann an anderer Stelle wiederverwendet werden, und wenn die Operation abgeschlossen ist, können Sie den Thread verwenden, der zu diesem Zeitpunkt gerade verfügbar ist.

Es ist wahrscheinlich auch lesenswert über und Annahme these Best Practices. Wahrscheinlich möchten Sie .ConfigureAwait(false) überall dort verwenden, wo Sie nicht auf Kontextinformationen wie Sitzungen und Anfragen zugreifen.

Diese Antwort setzt natürlich voraus, dass GetById von Natur aus asynchron ist: Sie rufen es von einer Festplatte oder einem Netzwerkstandort oder etwas ab. Wenn Sie die ID des Benutzers mit einer lang andauernden CPU-Operation berechnen, ist Task.Run() ein guter Weg zu gehen, und Sie werden wahrscheinlich zusätzlich angeben wollen, dass es eine langwierige Aufgabe in den Argumenten Task.Run() ist.

+0

Ich berechne 'id' nicht, ich suche einen Benutzer in einer DB mit einer bestimmten ID, und vielleicht hat die DB Millionen von Benutzern und keinen Index auf 'Id' – ProfK

+0

@ProfK: Seit Ihrem Datenbank-Back-End unterstützt asynchrone Tasks nicht, warum versuchen Sie, Ihre GetByIdAsync-Methode von vornherein asynchron zu machen? Wenn der Anrufer während der Ausführung zu einer anderen Aufgabe wechseln kann, muss der Anrufer dafür verantwortlich sein, 'Task.Run()' aufzurufen. (Siehe http://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.html) Sind Sie einfach zukunftssicher in Ihrem Code, in der Hoffnung, dass dies eines Tages asynchron sein wird? In diesem Fall 'return Task.FromResult (_userService.GetById (id));'. Sonst gibt es keinen Grund Async zu verwenden, also nicht. – StriplingWarrior

0

Task.Run() sollte nur für CPU-gebundene Arbeit verwendet werden. Erinnere dich nicht genau warum. Versuchen Sie stattdessen, eine GetByIdAsync() -Methode zu erstellen, die letztendlich eine asynchrone Ressource aufruft.

+0

Ich habe keine asynchrone Ressource, die Ressource ist NHibernate, die, soweit ich herausgefunden habe, async noch nicht unterstützt. – ProfK