web-dev-qa-db-fra.com

Coroutines en C #

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

Existe-t-il un "bon" moyen d'implémenter des coroutines en c #? 

J'ai pensé implémenter ceci en utilisant des threads qui acquièrent un mutex d'exécution unique + 1 sur le thread du planificateur qui libère ce mutex pour chaque coroutine. Mais cela semble très coûteux (cela force un changement de contexte entre chaque coroutine)

J'ai également vu la fonctionnalité d'itérateur de rendement, mais si je comprends bien, vous ne pouvez pas céder au sein d'une fonction interne (uniquement dans la fonction de numériseur d'origine). Alors ça me fait un peu de bien.

40
eyal

Edit: Vous pouvez maintenant utiliser ceux-ci: Existe-t-il une API de fibres dans .net?

Je crois que vous devriez regarder le Reactive Extensions for .NET . Par exemple, coroutines peut être simulé à l'aide d'itérateurs et de l'instruction return .

Cependant, vous voudrez peut-être lire cette question SO aussi.

12
Preet Sangha

Je pense qu'avec le nouveau .NET 4.5\C # 5, le modèle async\wait 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 suggère de regarder http://channel9.msdn.com/Events/TechEd/Australia/Tech-Ed-Australia-2011/DEV411 pour plus d'informations. C'est une excellente présentation.

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

Si vous utilisez une version plus ancienne de .NET, un CTP asynchrone est disponible pour les versions plus anciennes .NET avec une licence active 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 pourriez suivre le modèle d'itérateur asynchrone décrit ci-dessous. http://www.Microsoft.com/download/en/details.aspx?displaylang=en&id=9983

9
Aaron Stainback

Here est un exemple d'utilisation de threads pour implémenter des coroutines:

Alors je triche. J'utilise des threads, mais je seulement laissez l'un d'entre eux courir à la fois. Quand je créer un coroutine, je crée un fil, et ensuite faire une poignée de main qui se termine avec un appel à Monitor.Wait (), qui bloque le fil de la coroutine - il ne le sera pas ne marche plus jusqu’à ce qu’il soit dégagé. Quand il est temps de faire appel à la coroutine, Je fais un transfert qui se termine par le appelant bloqué, et le fil coroutine exécutable. Même type du transfert au retour.

Ces transferts coûtent cher, comparé à d'autres implémentations . Si vous avez besoin de vitesse, vous voudrez écrivez votre propre machine d'état, et éviter tout ce changement de contexte. (Ou Vous voudrez utiliser un runtime sensible à la fibre optique - le changement de fibre est joli Pas cher.) Mais si vous voulez expressif code, je pense que les coroutines en tiennent. promettre.

6
SLaks

Canalise la pièce manquante

Les pipelines sont la pièce manquante par rapport aux canaux dans le golang. Les chaînes sont en réalité ce qui fait golang tick. Les canaux sont le principal outil de concurrence. Si vous utilisez quelque chose comme une coroutine en C # mais utilisez des primitives de synchronisation de threads (sémaphore, moniteur, imbriqué, etc.), alors ce n'est pas la même chose.

Presque pareil - Pipelines, mais cuits au four

8 ans plus tard, et .Net Standard (.Net Framework/.Net Core) prend en charge les pipelines [ https://blogs.msdn.Microsoft.com/dotnet/2018/07/09/system-io-pipelines- haute performance-io-in-net /] . Les pipelines sont préférés pour le traitement du réseau. Aspcore figure désormais parmi les 11 principaux taux de demande de débit en texte brut [ https://www.techempower.com/benchmarks/#section=data-r16&hw=ph&test=plaintext]

Microsoft conseille de créer une interface 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 les lire de manière asynchrone. De nombreux pipelines peuvent être utilisés en série pour divers processus sur le flux d'octets. Le pipeline comporte un lecteur et un curseur d'écriture, et la taille de la mémoire tampon virtuelle provoque 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 existe des différences critiques entre les pipelines et les canaux Go. Les pipelines ne ressemblent pas à un canal de golang. Les pipelines consistent à transmettre des octets mutables plutôt que des canaux golang destinés à la signalisation avec des références de mémoire (y compris des pointeurs). Enfin, il n'y a pas d'équivalent select avec Pipelines.

(Les pipelines utilisent des étendues [ https://adamsitnik.com/Span/] , qui existent depuis un moment, mais qui sont maintenant optimisées en profondeur dans .Net Core. Les étendues améliorent considérablement les performances. Le support .Net Core améliore encore les performances, mais de manière incrémentielle, de sorte que l'utilisation de .Net Framework convient parfaitement.)

Les pipelines sont donc une norme intégrée qui devrait aider à remplacer les canaux de golang dans .Net, mais ils ne sont pas identiques et il y aura de nombreux cas où les pipelines ne sont pas la solution.

Implémentations directes du canal de Golang

Vous devez faire attention (comme avec golang) que les messages passés via un canal .Net indiquent un changement de propriété sur un objet. C’est quelque chose que seul un programmeur peut suivre et vérifier, et si vous vous trompez, vous aurez au moins deux threads accédant aux données sans synchronisation.

0
Todd

Vous pouvez être intéressé par this est une bibliothèque qui cache l’utilisation de coroutines. Par exemple pour lire un fichier:

//Prepare the file stream
FileStream sourceStream = File.Open("myFile.bin", FileMode.OpenOrCreate);
sourceStream.Seek(0, SeekOrigin.End);

//Invoke the task
yield return InvokeTaskAndWait(sourceStream.WriteAsync(result, 0, result.Length));

//Close the stream
sourceStream.Close();

Cette bibliothèque utilise un seul thread pour exécuter toutes les coroutines et permettre d’appeler la tâche pour les opérations réellement asynchrones. Par exemple, appeler une autre méthode comme coroutine (cédant pour son retour

//Given the signature
//IEnumerable<string> ReadText(string path);

var result = new Container();
yield return InvokeLocalAndWait(() => _globalPathProvider.ReadText(path), container);
var data = container.RawData as string;
0
Kendar