C'est une très bonne question, et la compréhension est la clé pour comprendre pourquoi asynchrone IO est si important. La raison pour laquelle la nouvelle async/await fonctionnalité a été ajoutée à C# 5.0 est de simplifier l'écriture de code asynchrone. Soutien pour le traitement asynchrone sur le serveur est cependant pas nouveau, il existe depuis ASP.NET 2.0.
Comme Steve vous a montré, avec un traitement synchrone, chaque demande ASP.NET (WCF) prend un fil de la piscine. La question qu'il fait des démos est une question bien connue appelée "pool de threads de la famine". Si vous faites synchrone IO sur votre serveur, le thread du pool restera bloquée (ne rien faire) pour la durée de l'OI. Depuis il y a une limite dans le nombre de threads dans le pool de threads, sous la charge, ce qui peut conduire à une situation où tous les threads du pool de threads sont bloqués en attente d'e / s, et la demande commence à être mis en file d'attente, ce qui provoque une augmentation de temps de réponse. Depuis, tous les threads sont en attente d'une e / s, vous verrez un CPU d'occupation proche de 0% (même si les temps de réponse de passer par le toit).
Ce que vous demandez (Pourquoi ne peut-on pas utiliser un plus gros pool de threads?) est une très bonne question. Comme une question de fait, c'est comment la plupart des gens ont été de résoudre le problème de pool de threads de la famine jusqu'à présent: plus de threads sur le pool de threads. Une partie de la documentation de Microsoft indique même qu'un correctif pour les situations où le pool de thread la famine peut se produire. C'est une solution acceptable, et jusqu'à C# 5.0, il était beaucoup plus facile à faire, que de réécrire votre code pour être totalement asynchrone.
Il y a quelques problèmes avec l'approche:
Il n'y a pas de valeur qui fonctionne dans toutes les situations: le nombre de threads du pool de threads que vous allez avoir besoin dépend linéairement sur la durée de l'OI, et la charge sur votre serveur. Malheureusement, IO latence est la plupart du temps imprévisibles. Voici un exemple:
Disons que vous faire des requêtes HTTP à un tiers de service web dans votre ASP.NET l'application, qui prend environ 2 secondes. Vous rencontrez pool de threads de la famine, si vous décidez d'augmenter la taille du pool de threads, disons, 200 fils, et puis il commence à travailler à nouveau. Le problème, c'est que peut-être la semaine prochaine, le service web de problèmes techniques, ce qui augmente leur temps de réponse de 10 secondes. Tout d'un coup, le pool de thread la famine est de retour, parce que les threads sont bloqués à 5 fois plus longtemps, vous avez maintenant besoin d'augmenter le nombre 5 fois, à 1000 fils.
L'évolutivité et les performances: Le deuxième problème est que si vous faites cela, vous pourrez toujours utiliser un thread par demande. Les Threads sont une ressource coûteuse. Chaque thread géré .NET requiert l'un de l'allocation de mémoire de 1 MO pour la pile. Pour une page web faisant IO qui durent 5 secondes, et avec une charge de 500 requêtes par seconde, vous aurez besoin de 2 500 threads dans votre pool de threads, ce qui signifie 2,5 GO de mémoire pour les piles de threads qui sera assis à ne rien faire. Vous avez alors le problème de la commutation de contexte, qui va prendre un lourd tribut sur les performances de votre machine (touchant tous les services sur la machine, et pas seulement votre application web). Même si Windows fait un assez bon travail à ignorer les threads en attente, il n'est pas conçu pour supporter un grand nombre de threads. Rappelez-vous que le rendement le plus élevé est obtenu lorsque le nombre de threads en cours d'exécution est égal au nombre de Processeurs logiques sur la machine (généralement pas plus de 16 ans).
L'augmentation de la taille du pool de threads est une solution, et les gens l'ont fait que pour une dizaine d'années (même dans les propres produits de Microsoft), il est un peu moins évolutif et efficace, en termes de mémoire et d'utilisation de l'UC, et vous êtes toujours à la merci d'une hausse soudaine de IO latence qui serait la cause de la famine. Jusqu'en C# 5.0, la complexité du code asynchrone n'était pas la peine pour de nombreuses personnes. async/await change tout comme maintenant, vous pouvez profiter de l'évolutivité de l'asynchrone IO, et d'écrire du code simple, dans le même temps.
Plus de détails: http://msdn.microsoft.com/en-us/library/ff647787.aspx "Utiliser des appels asynchrones à invoquer des services Web ou des objets à distance quand il y a la possibilité d'effectuer un parallèle supplémentaire de traitement alors que l'appel de service Web produit. Si possible, éviter synchrone (blocage) des appels à des services Web, car sortant des appels de service Web sont fabriqués en utilisant des fils de l'ASP.NET pool de threads. Blocage des appels de réduire le nombre de threads disponibles pour le traitement des autres demandes entrantes."