même après avoir utilisé ConfigureAwait (false) dans Asp.Net flux

je suis frapper impasse, même après l'utilisation de ConfigureAwait(false), ci-dessous le code de l'échantillon.

selon l'échantillon http://blog.stephencleary.com/2012/02/async-and-await.html (contexte # Avoding), cela n'aurait pas dû frapper dead lock.

C'est ma classe:

public class ProjectsRetriever
{
    public string GetProjects()
    {
        ...
        var projects = this.GetProjects(uri).Result;
        ...
        ...
    }

    private async Task<IEnumerable<Project>> GetProjects(Uri uri)
    {
        return await this.projectSystem.GetProjects(uri, Constants.UserName).ConfigureAwait(false);
    }
}

Cette classe est à partir d'une bibliothèque partagée:

public class ProjectSystem
{
    public async Task<IEnumerable<Project>> GetProjects(Uri uri, string userName)
    {
        var projectClient = this.GetHttpClient<ProjectHttpClient>(uri);
        var projects = await projectClient.GetProjects();
        // code here is never hit
        ...
}

si j'ajoute ConfigureAwait(false) pour attendre l'appel dans la bibliothèque partagée, où HttpClient appel est fait:

public class ProjectSystem
{
    public async Task<IEnumerable<Project>> GetProjects(Uri uri, string userName)
    {
        var projectClient = this.GetHttpClient<ProjectHttpClient>(uri);
        var projects = await projectClient.GetProjects().ConfigureAwait(false);
        // no deadlock, resumes in a new thread.
        ...
}

j'ai parcouru tous les blogs trouvés, la seule différence que je trouve est ConfigureAwait(false) works lorsqu'il est utilisé avec httpClient.AsyncApi() l'appel!?

s'il vous Plaît aider clarifier!!!

12
demandé sur Yuval Itzchakov 2014-08-31 07:18:14

3 réponses

commentaires:

j'étais dans l'hypothèse, une fois ConfigureAwait(false) utilisé (n'importe où dans la pile d'appels), l'exécution à partir de ce point ne causera pas d'impasse.

Je ne crois pas à la magie noire, et vous non plus. Toujours s'efforcer de comprendre ce qui se passe lorsque vous utilisez quelque chose dans votre code.

quand vous await une méthode asynchrone qui renvoie un Task et SynchronizationContext par TaskAwaitable généré par l' Task.GetAwaiter méthode.

une fois que le contexte de synchronisation est en place et que l'appel de méthode async est terminé, le TaskAwaitable tente de canaliser la suite (qui est essentiellement le reste des appels de méthode après le premier await mot clé) sur le SynchronizationContext(en utilisant SynchronizationContext.Post) qui a déjà été capturé. Si le thread d'appel est bloqué, en attendant cette même méthode pour finir, vous avez un blocage.

vous devriez demandez-vous dois-je exposer des enveloppes synchrones pour des méthodes asynchrones? 99% du temps la réponse est aucun. Vous devez utiliser une API synchrone, comme celle WebClient offres.

16
répondu Yuval Itzchakov 2014-08-31 11:09:31

il bloque lorsqu'il est utilisé dans ProjectsRetriever parce que:

public class ProjectsRetriever
{
    public string GetProjects()
    {
        //querying the result blocks the thread and wait for result.
        var projects = this.GetProjects(uri).Result;
        ... //require Thread1 to continue.
        ...
    }

    private async Task<IEnumerable<Project>> GetProjects(Uri uri)
    {
        //any thread can continue the method to return result because we use ConfigureAwait(false)
        return await this.projectSystem.GetProjects(uri, Constants.UserName).ConfigureAwait(false);
    }
}

public class ProjectSystem
{
    public async Task<IEnumerable<Project>> GetProjects(Uri uri, string userName)
    {
        var projectClient = this.GetHttpClient<ProjectHttpClient>(uri);
        var projects = await projectClient.GetProjects();
        // code here is never hit because it requires Thread1 to continue its execution
        // but Thread1 is blocked in var projects = this.GetProjects(uri).Result;
        ...
}

Il ne bloque pas quand utilisé dans ProjectSystem parce que:

public class ProjectsRetriever
{
    public string GetProjects()
    {
        ...
        var projects = this.GetProjects(uri).Result;
        ...//requires Thread1 to continue
        ...
    }

    private async Task<IEnumerable<Project>> GetProjects(Uri uri)
    {
        //requires Thread1 to continue
        return await this.projectSystem.GetProjects(uri, Constants.UserName);
    }
}

public class ProjectSystem
{
    public async Task<IEnumerable<Project>> GetProjects(Uri uri, string userName)
    {
        var projectClient = this.GetHttpClient<ProjectHttpClient>(uri);
        var projects = await projectClient.GetProjects().ConfigureAwait(false);
        // no deadlock, resumes in a new thread. After this function returns, Thread1 could continue to run
}
7
répondu Khanh TO 2014-08-31 04:24:30

j'ai eu le même problème. "ConfigureAwait (false)" ne peut pas toujours éviter les serrures mortes.

public class HomeController : Controller
{
    public async Task<ActionResult> Index()
    {
        // This works !
        ViewBag.Title = GetAsync().Result;

        // This cause deadlock even with "ConfigureAwait(false)" !
        ViewBag.Title = PingAsync().Result;

        return View();
    }

    public async Task<string> GetAsync()
    {
        var uri = new Uri("http://www.google.com");
        return await new HttpClient().GetStringAsync(uri).ConfigureAwait(false);
    }

    public async Task<string> PingAsync()
    {
        var pingResult = await new Ping().SendPingAsync("www.google.com", 3).ConfigureAwait(false);

        return pingResult.RoundtripTime.ToString();
    }
}

pour le code ci-dessus, " GetAsync () "fonctionne alors que" PingAsync () " ne fonctionne pas.

mais j'ai trouvé que si j'enroule l'appel async dans une nouvelle tâche, et attend cette tâche, PingAsync () fonctionnera l'événement sans "ConfigureAwait (false)":

var task = Task.Run(() => PingAsync());
task.Wait();
ViewBag.Title = task.Result;

je ne sais pas pourquoi, peut-être quelqu'un peut me dire la différence.

0
répondu Meng Xue 2018-06-23 23:54:53