52 votes

Coroutines en C#

Je cherche des moyens d'implémenter des co-routines (threads programmés par l'utilisateur) en C#. Lorsque j'utilisais C++, j'utilisais des fibres. Je vois sur Internet que les fibres n'existent pas en C#. J'aimerais obtenir une fonctionnalité similaire.

Existe-t-il une "bonne" façon d'implémenter des coroutines en C# ?

J'ai pensé à l'implémenter en utilisant des threads qui acquièrent un seul mutex d'exécution + 1 thread d'ordonnancement qui libère ce mutex pour chaque coroutine. Mais cela semble très coûteux (cela oblige à un changement de contexte entre chaque coroutine).

J'ai également vu la fonctionnalité d'itérateur de rendement, mais d'après ce que j'ai compris, il n'est pas possible d'effectuer un rendement dans une fonction interne (uniquement dans la fonction ienumerator originale). Cela ne me sert donc pas à grand-chose.

0 votes

0 votes

Vous trouverez peut-être ceci utile/intéressant : github.com/bvanderveen/coroutine

16voto

Aaron Stainback Points 974

Je crois qu'avec le nouveau .NET 4.5 \C # 5 l'asynchrone \await devrait répondre à vos besoins.

async Task<string> DownloadDocument(Uri uri)
{  
  var webClient = new WebClient();
  var doc = await webClient.DownloadStringTaskAsync(url);
  // do some more async work  
  return doc;  
}  

Je vous suggère de regarder http://channel9.msdn.com/Events/TechEd/Australia/Tech-Ed-Australia-2011/DEV411 pour plus d'informations. Il s'agit d'une excellente présentation.

Aussi http://msdn.microsoft.com/en-us/vstudio/gg316360 a de très bonnes informations.

Si vous utilisez une version plus ancienne de .NET, il existe un CTP Async disponible pour les anciennes versions de .NET avec une licence de mise en service afin que vous puissiez l'utiliser dans des environnements de production. Voici un lien vers le CTP http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=9983

Si vous n'aimez pas l'une ou l'autre des options ci-dessus, je pense que vous pouvez suivre le modèle de l'itérateur asynchrone tel que décrit ici. http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=9983

13voto

Preet Sangha Points 39414

Edit : Vous pouvez maintenant les utiliser : Existe-t-il un api pour les fibres en .net ?

Je pense que vous devriez regarder le Extensions réactives pour .NET . Par exemple les coroutines peuvent être simulées en utilisant des itérateurs et le rendement déclaration.

Cependant, vous voudrez peut-être lire ceci Question aussi.

0 votes

Les blocs d'itérateurs ne sont plus pertinents depuis Rx 2.0, qui fournit maintenant des surcharges de Observable.Create qui acceptent un Task -Ce faisant, la création d'une observable (call/cc) est confondue avec les coroutines natives de C# 5 ( async/await ). Ceci peut être défini comme un "itérateur asynchrone". Voir ce fil pour plus d'informations et mon blog post pour une comparaison.

0 votes

Hmm, ok merci. Je suis assez nouveau sur SO et je n'étais pas sûr de l'étiquette wiki. Je vais d'abord lire les "règles".

0 votes

Ne vous inquiétez pas, l'essentiel est que si vous pouvez améliorer quelque chose, vous devez le faire.

6voto

Noseratio Points 23840

Nous sommes en 2020, beaucoup de choses ont évolué en C#. J'ai publié un article sur ce sujet, Coroutines asynchrones avec C# 8.0 et IAsyncEnumerable :

Dans le monde du C#, elles (coroutines) ont été popularisées par Unity développement de jeux plateforme et Unity utilise IEnumerator -style et yield return pour ça.

Avant C# 8, il n'était pas possible de combiner await y yield return dans la même méthode, ce qui rend difficile l'utilisation de l'asynchronisme à l'intérieur des coroutines. Désormais, grâce à la prise en charge par le compilateur de la fonction IAsyncEnumerable il est possible de le faire naturellement.

6voto

SLaks Points 391154

Ici est un exemple d'utilisation de threads pour mettre en œuvre des coroutines :

Alors je triche. J'utilise des threads, mais je ne mais je n'en laisse tourner qu'un à la fois. Quand je crée une coroutine, je crée un thread, et ensuite je fais quelques manipulations qui se terminent avec un appel à Monitor.Wait(), qui bloque le thread de la coroutine - il ne s'exécutera pas ne s'exécutera plus tant qu'il ne sera pas débloqué. Lorsque il est temps d'appeler la coroutine, je fais un handoff qui se termine avec le thread appelant est bloqué, et le fil de la coroutine exécutable. Même type de transfert sur le chemin du retour.

Ces transferts sont assez coûteux, comparé à d'autres implémentations. Si vous avez besoin de rapidité, vous voudrez peut-être écrire votre propre machine d'état, et et éviter tout ce changement de contexte. (Ou alors vous voudrez utiliser un runtime sensible à la fibre fibres - la commutation de fibres est assez bon marché). Mais si vous voulez un code expressif expressif, je pense que les coroutines sont promesses.

3 votes

Une limitation de l'utilisation des threads est que (dans Winforms et WPF, je crois) les contrôles d'un formulaire qui a été créé par le thread Larry ne peut pas être accédé par le fil Moe même si le fil Larry ne fait rien avec le formulaire et sera bloqué jusqu'à ce que Moe n'en fait rien non plus. Si deux coroutines pouvaient s'exécuter sur le même thread du système d'exploitation, elles pourraient partager des ressources qui ne sont autrement utilisables que par le thread créateur.

4voto

Todd Points 2386

Les canaux, la pièce manquante

Les pipelines sont la pièce manquante par rapport aux canaux dans golang. Les canaux sont en fait ce qui fait fonctionner golang. Les channels sont l'outil de base de la concurrence. Si vous utilisez quelque chose comme une coroutine en C# mais en utilisant les primitives de synchronisation des threads (sémaphore, moniteur, interlocked, etc.) alors ce n'est pas la même chose.

Presque la même chose - Pipelines, mais intégrés dans le système

8 ans plus tard, et .Net Standard (.Net Framework / .Net Core) a le support pour les Pipelines [ [https://blogs.msdn.microsoft.com/dotnet/2018/07/09/system-io-pipelines-high-performance-io-in-net/\]](https://blogs.msdn.microsoft.com/dotnet/2018/07/09/system-io-pipelines-high-performance-io-in-net/]) . Les pipelines sont préférés pour le traitement en réseau. Aspcore se classe maintenant parmi les 11 meilleurs taux de demande de débit en texte clair [ [https://www.techempower.com/benchmarks/#section=data-r16&hw=ph&test=plaintext\]](https://www.techempower.com/benchmarks/#section=data-r16&hw=ph&test=plaintext]) .

Microsoft conseille les meilleures pratiques pour l'interfaçage avec le trafic réseau : les octets réseau attendus (Completion Port IO) doivent placer les données dans un pipeline, et un autre thread doit lire les données du pipeline de manière asynchrone. Plusieurs pipelines peuvent être utilisés en série pour divers processus sur le flux d'octets. Le pipeline possède un curseur de lecture et un curseur d'écriture, et la taille de la mémoire tampon virtuelle provoquera une contre-pression sur l'écrivain afin de réduire l'utilisation inutile de la mémoire pour la mise en mémoire tampon, ce qui ralentit généralement le trafic réseau.

Il y a quelques différences essentielles entre les Pipelines et les Canaux Go. Les pipelines ne sont pas les mêmes qu'un canal Go. Les pipelines servent à passer des octets mutables plutôt que les canaux golang qui servent à signaler des références mémoire (y compris les pointeurs). Enfin, il n'y a pas d'équivalent select avec les pipelines.

(Les pipelines utilisent des travées [ [https://adamsitnik.com/Span/\]](https://adamsitnik.com/Span/]) qui existent depuis un certain temps, mais qui sont maintenant optimisées en profondeur dans .Net Core. Les portées améliorent considérablement les performances. La prise en charge de .Net Core améliore encore les performances, mais seulement de manière incrémentielle, de sorte que l'utilisation de .Net Framework est parfaitement acceptable. )

Les pipelines sont donc un standard intégré qui devrait aider à remplacer les canaux golang dans .Net, mais ils ne sont pas identiques, et il y aura beaucoup de cas où les pipelines ne sont pas la réponse.

Implémentations directes du canal Golang

Vous devez faire attention (comme avec golang) à ce que les messages transmis via un canal .Net indiquent un changement de propriété d'un objet. C'est quelque chose que seul un programmeur peut suivre et vérifier, et si vous vous trompez, vous aurez deux ou plusieurs threads accédant aux données sans synchronisation.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X