Soutenez-nous

Utilisez-vous le nouveau modèle de programmation asynchrone de C# 5/VB 11 ?
Qu'en pensez-vous ? Participez au débat et partagez votre expérience

Les rubriques (actu, forums, tutos) de Développez
Réseaux sociaux


 Discussion forum

Sur le même sujet
Le , par tomlev, Rédacteur/Modérateur


Utilisez-vous le nouveau modèle de programmation asynchrone de C# 5/VB 11 ?


Comme vous le savez sans doute, la version 5 de C# (comme la version 11 de VB.NET) a introduit de nouveaux mots-clés pour faciliter l'écriture de code asynchrone : async et await. Ce nouveau modèle permet d'écrire très simplement du code qui ne bloque pas l'UI et ne monopolise pas inutilement des threads de travail.

Une introduction à la programmation asynchrone en C# 5, par Eric Lippert

Par exemple, pour télécharger des données depuis le web :

  • code synchrone (bloque l'UI pendant l'exécution de la requête) :

    Code C# :
    1
    2
    3
    4
    5
    6
    private void btnDownload_Click(object sender, EventArgs e) 
    { 
        var client = new WebClient(); 
        string data = client.DownloadString("http://monserveur.com/data"); 
        txtData.Text = data; 
    }
  • code asynchrone « classique » :

    Code C# :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    private void btnDownload_Click(object sender, EventArgs e) 
    { 
        var client = new WebClient(); 
        client.DownloadStringCompleted += client_DownloadStringCompleted; 
        client.DownloadStringAsync("http://monserveur.com/data"); 
    } 
      
    private void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) 
    { 
        txtData.Text = e.Result; 
    }

    Ce code est nettement moins lisible ; le déclenchement de la requête est séparé de la récupération du résultat, ce qui rend le déroulement plus difficile à suivre.
  • code asynchrone C# 5 :

    Code C# :
    1
    2
    3
    4
    5
    6
    private async void btnDownload_Click(object sender, EventArgs e) 
    { 
        var client = new WebClient(); 
        string data = await client.DownloadStringTaskAsync("http://monserveur.com/data"); 
        txtData.Text = data; 
    }

    On remarque que ce code est quasiment identique au code synchrone de départ ; on l'a rendu asynchrone, sans rien sacrifier à la lisibilité.


Microsoft pousse de plus en plus à l'utilisation de ce modèle, notamment sur Windows Phone 8 et dans les applications Windows Store (Metro) ; bien souvent, seules des API asynchrones sont proposées, et les API synchrones habituelles sont supprimées si elles existaient.

Pourtant, il me semble qu'on voit assez peu de discussions qui abordent ce sujet sur les forums, ce qui me pousse à ouvrir ce débat…

Et vous ?

Utilisez-vous le nouveau modèle de programmation asynchrone ? En C# ou en VB.NET ? Sur quelle plateforme (bureau, Windows Phone, WinRT, ASP.NET…) ?
Si non, pourquoi ? Éprouvez-vous des difficultés à comprendre son fonctionnement ?
Si oui, qu'en pensez-vous ? Comment a-t-il changé votre façon de programmer ? Quelles difficultés avez-vous rencontrées lors de son utilisation ?

À lire aussi :

Quel est votre langage de programmation préféré en 2013 ?

Quel est votre environnement de développement (EDI) préféré en 2013 ?



 Poster une réponse

Avatar de tomlev tomlev
Rédacteur/Modérateur
le 31/07/2013 14:10
Bon, je me lance...

Utilisez-vous le nouveau modèle de programmation asynchrone ? En C# ou en VB.NET ? Sur quelle plateforme (bureau, Windows Phone, WinRT, ASP.NET…) ?

Je l'utilise sur la plupart de mes projets récents, principalement pour des applis Windows Store (WinRT), mais aussi pour des applis bureau.

Détail intéressant : il n'est pas forcément nécessaire de cibler .NET 4.5 ou Windows Phone 8 pour pouvoir utiliser async/await. A condition d'utiliser VS2012 (et donc le compilateur C# 5) et d'ajouter le package Nuget Microsoft.Bcl.Async, on peut également cibler également des environnements plus anciens, comme .NET 4, Silverlight 4 ou Windows Phone 7.5

Si oui, qu'en pensez-vous ? Comment a-t-il changé votre façon de programmer ? Quelles difficultés avez-vous rencontrées lors de son utilisation ?

Personnellement je suis plutôt convaincu par le modèle async/await. Ça rend la programmation asynchrone tellement facile qu'on en fait sans même y penser, alors qu'avant beaucoup de traitements étaient faits en synchrone par simple flemme, parce que les faire de façon asynchrone était trop "prise de tête".

Par contre il y a un effet pervers : le code asynchrone est assez "contagieux". Il est difficile de faire une partie des traitements en asynchrone sans que ça "envahisse" un peu le reste du code. En effet, dès qu'on appelle une méthode asynchrone :
  • soit on "await" le résultat et dans ce cas on devient donc asynchrone aussi
  • soit on bloque en attendant le résultat, mais dans ce cas on perd l'avantage de l'appel asynchrone
  • soit on fait du "fire and forget", c'est à dire qu'on lance l'opération asynchrone sans se soucier de son résultat

On peut donc se retrouver assez vite avec du code asynchrone à tous les niveaux du code, y compris à des endroits où ça ne semblait pas nécessaire a priori

Sinon, au niveau des difficultés, il y a pas mal de situations où on ne peut pas utiliser de code asynchrone, tout simplement parce que le langage n'avait pas été prévu pour ça au départ : constructeurs, propriétés, évènements, blocs catch et finally, etc. Ça oblige souvent à faire des trucs bizarres pour arriver au résultat voulu.
(voir cette série d'articles sur le sujet: http://blog.stephencleary.com/search/label/async%20oop)

D'autre part, j'ai l'impression qu'il y a une idée reçue assez répandue selon laquelle le code asynchrone implique forcément l'utilisation de plusieurs threads. Mais ce n'est absolument pas vrai dans le cas général ; certains traitements asynchrones vont effectivement s'exécuter sur un autre thread, mais bien souvent ce n'est pas nécessaire. Par exemple les opérations d'entrée/sortie asynchrone n'utilisent pas de thread de travail : la partie "attente" n'implique pas du tout le processeur, c'est un IO Completion Port qui va notifier la fin de la tâche. D'ailleurs on peut facilement trouver des scénarios asynchrones qui n'ont absolument rien à voir avec les threads ; par exemple ce code attend (sans bloquer) que l'utilisateur clique sur un bouton :

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
async void Test() 
{ 
    await button.WaitForClickAsync(); 
    MessageBox.Show("Hello"); 
} 
 
public static class ButtonExtensions 
{ 
    public static Task WaitForClickAsync(this Button button, CancellationToken cancellationToken = default(CancellationToken)) 
    { 
		var tcs = new TaskCompletionSource<object>(); 
                EventHandler handler = null; 
		handler = (sender, e) => 
		{ 
			button.Click -= handler; 
			tcs.TrySetResult(null); 
		}; 
		cancellationToken.Register(() => 
		{ 
			button.Click -= handler; 
			tcs.TrySetCanceled(); 
		}); 
		 
		button.Click += handler; 
		return tcs.Task; 
    } 
}
Avatar de transgohan transgohan
Expert Confirmé Sénior
le 08/08/2013 14:27
Je bosse pas sur cette technologie mais en lisant ça j'essaie de comprendre.
Cela ne me paraît pas tiptop, mais je peux me tromper sans avoir les bons éléments d'explication.

Dans l'exemple fourni avec await, comment sait-on que txtData.Text a été mis à jour à la suite du traitement asynchrone ?
Et je vois surtout une saleté dans ce code, c'est un piège pour tous les débutants qui vont croire à un code synchrone à la première lecture et s'embourber jusqu'à trouver leur connerie (le petit await qu'ils avaient lu en diagonale)... (et là ça peut durer longtemps !)
Avatar de tomlev tomlev
Rédacteur/Modérateur
le 08/08/2013 14:37
Citation Envoyé par transgohan  Voir le message
Dans l'exemple fourni avec await, comment sait-on que txtData.Text a été mis à jour à la suite du traitement asynchrone ?

C'est-à-dire ? Je ne comprends pas ta question. Quand l'appel asynchrone à DownloadStringTaskAsync se termine, la fonction reprend là où elle s'était interrompue, et exécute le txtData.Text = data;.

Citation Envoyé par transgohan  Voir le message
Et je vois surtout une saleté dans ce code, c'est un piège pour tous les débutants qui vont croire à un code synchrone à la première lecture et s'embourber jusqu'à trouver leur connerie (le petit await qu'ils avaient lu en diagonale)... (et là ça peut durer longtemps !)

Bah oui mais bon, si tu fais pas attention au code que tu lis, évidemment ça pose des problèmes... Tu ne peux pas dire qu'une feature est mauvaise juste parce qu'elle risque de poser des problèmes aux débutants.
Avatar de erwanlb erwanlb
Inactif
le 08/08/2013 14:47
Citation Envoyé par transgohan  Voir le message
Je bosse pas sur cette technologie mais en lisant ça j'essaie de comprendre.
Cela ne me paraît pas tiptop, mais je peux me tromper sans avoir les bons éléments d'explication.

Dans l'exemple fourni avec await, comment sait-on que txtData.Text a été mis à jour à la suite du traitement asynchrone ?
Et je vois surtout une saleté dans ce code, c'est un piège pour tous les débutants qui vont croire à un code synchrone à la première lecture et s'embourber jusqu'à trouver leur connerie (le petit await qu'ils avaient lu en diagonale)... (et là ça peut durer longtemps !)

Vu la première question, tu serais le débutant en question....et tu vois tu as bien vu le await

Si maintenant il faut prévenir les débutants de lire un code en entier, où est ce qu'on va............
Avatar de koyosama koyosama
Membre habitué
le 08/08/2013 15:09
Ce truc m'a juste sauvé la vie. J'avais un problème d'écriture et génération de fichiers supra long. Et il écrivait trop lentement comparé à mon exécution de code.

Faire des éxécutions de tâche synchrone parraisait super chiant.

Maintenant c'est vraiment simple. Mais pour revenir en haut. Effectivement les tâches parallèles et asynchrone c'est pas trop fait pour les débutants.

Et c'est vachement outils pour gérer les streams qui met du temps à s’exécuter.
Avatar de frfancha frfancha
Membre confirmé
le 08/08/2013 15:19
Je l'utilise tout le temps (je ne connais que cela j'ai commencé par .net 4.5).

Sauf pour le thread qui fait les queries vers une DB chaque fois que le user tape un caractère et qui gère une queue de requête.
Avatar de jmnicolas jmnicolas
Membre émérite
le 08/08/2013 15:36
Je n'utilise pas : je suis en .NET 4 avec VS 2010.
On a encore plein de machines sous XP / 2003 Server, et .NET 4 et la dernière version qui tourne dessus.
Avatar de clorr clorr
Invité de passage
le 08/08/2013 15:54
euh, moi je trouve pas ca tres pratique. Si j'ai bien compris, la fonction ne quitte pas tant que l'appel await n'a pas fini ? Donc ce n'est pas vraiment asynchrone ?
Avatar de transgohan transgohan
Expert Confirmé Sénior
le 08/08/2013 15:59
Citation Envoyé par tomlev  Voir le message
C'est-à-dire ? Je ne comprends pas ta question. Quand l'appel asynchrone à DownloadStringTaskAsync se termine, la fonction reprend là où elle s'était interrompue, et exécute le txtData.Text = data;.

Et donc on stockes dans une variable la valeur d'une variable qui ne contient pas encore la valeur attendue ? Je pense qu'il vaut mieux deviner une référence dans ce cas.
Ok soit... Donc cela reprend ce que j'avais compris.
Mais ma question était tout autre... COMMENT sais-t-on que le traitement asynchrone a été exécuté...
Car stocker des valeurs c'est bien, mais exploiter celles que l'on attendait c'est mieux !

Ne prenez pas la mouche, je pense juste mettre en évidence ce qui n'est pas montré dans cet exemple. Et comme je ne programme pas en C# je peux pas l'inventer... Et j'ai franchement pas le temps d'aller lire la doc, donc j'en appelle à vos lumières.

Vu la première question, tu serais le débutant en question....et tu vois tu as bien vu le await

Et toi un mauvais pédagogue, c'est bien de me critiquer, mais m'expliquer c'est encore mieux.
Avatar de tomlev tomlev
Rédacteur/Modérateur
le 08/08/2013 16:03
Citation Envoyé par clorr  Voir le message
euh, moi je trouve pas ca tres pratique. Si j'ai bien compris, la fonction ne quitte pas tant que l'appel await n'a pas fini ? Donc ce n'est pas vraiment asynchrone ?

Non, tu n'as pas bien compris. Si c'était ça, ce serait pareil que du code synchrone et ça n'aurait pas d'intérêt

Reprenons le code de l'exemple :

Code :
1
2
3
4
5
6
private async void btnDownload_Click(object sender, EventArgs e) 
{ 
    var client = new WebClient(); 
    string data = await client.DownloadStringTaskAsync("http://monserveur.com/data"); 
    txtData.Text = data; 
}
Quand l'exécution arrive à await client.DownloadStringTaskAsync(...), le téléchargement est lancé en asynchrone, et le contrôle est immédiatement rendu au code qui a appelé btnDownload_Click ; on sort donc de la méthode au niveau du await. Quand le téléchargement se termine, la méthode btnDownload_Click reprend là où elle s'était interrompue : le résultat de DownloadStringTaskAsync est affecté à la variable data, et la suite continue de s'exécuter en synchrone jusqu'au prochain await (en l'occurrence il n'y en a pas)

Donc en gros, on sort de btnDownload_Click à chaque fois qu'on rencontre un await, et on y retourne à chaque fois que la tâche attendue par await se termine. A aucun moment le thread qui exécute btnDownload_Click ne reste bloqué à attendre la fin d'une tâche.
Offres d'emploi IT
Développeur Java/J2EE e-commerce
CDI
ECOCEA - Ile de France - Paris (75009)
Parue le 07/04/2014
Développeurs freelances
Mission
Itipart - Ile de France - Paris (75000)
Parue le 03/04/2014
Référent technique Java/JEE Alfresco H/F
CDI
BULL FR - Provence Alpes Côte d'Azur - Marseille (13000)
Parue le 11/04/2014

Voir plus d'offres Voir la carte des offres IT
 
 
 
 
Partenaires

PlanetHoster
Ikoula