Сопрограммы в C # - PullRequest
       47

Сопрограммы в C #

40 голосов
/ 08 апреля 2010

Я ищу способы реализации сопрограмм (пользовательских запланированных потоков) в c #.При использовании c ++ я использовал волокна.Я вижу в Интернете волокна не существуют в C #.Я хотел бы получить аналогичную функциональность.

Есть ли "правильный" способ реализации сопрограмм в c #?

Я думал о реализации этого с использованием потоков, которые получают один мьютекс выполнения + 1 в потоке планировщика, который освобождает этот мьютекс для каждой сопрограммы.Но это кажется очень дорогостоящим (оно вызывает переключение контекста между каждой сопрограммой)

Я также видел функциональность итератора yield, но, насколько я понимаю, вы не можете выполнить внутри внутренней функции (только в исходной функции ienumerator).Так что это мало мне помогает.

Ответы [ 5 ]

12 голосов
/ 08 апреля 2010

Редактировать: теперь вы можете использовать их: Есть ли в .net волоконно-оптический интерфейс?

Полагаю, вам стоит взглянуть на Реактивные расширения для .NET .Например, сопрограммы могут быть смоделированы с использованием итераторов и оператора yield .

Однако вы можете прочитать этот SO вопрос * и 1012 *.

9 голосов
/ 08 апреля 2012

Я полагаю, что с новым .NET 4.5 \ C # 5 шаблон асинхронизации \ await должен соответствовать вашим потребностям.

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

Я предлагаю посмотреть http://channel9.msdn.com/Events/TechEd/Australia/Tech-Ed-Australia-2011/DEV411 для получения дополнительной информации.Это отличная презентация.

Также http://msdn.microsoft.com/en-us/vstudio/gg316360 содержит много полезной информации.

Если вы используете более старую версию .NET, для старых версий .NET имеется Async CTP.с лицензией Go Live, чтобы вы могли использовать ее в производственных условиях.Вот ссылка на CTP http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=9983

Если вам не нравится ни один из вышеперечисленных вариантов, я думаю, вы могли бы следовать шаблону асинхронного итератора, как описано здесь.http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=9983

6 голосов
/ 08 апреля 2010

Здесь - пример использования потоков для реализации сопрограмм:

Так что я обманываю.Я использую потоки, но я запускаю только один из них за раз.Когда я создаю сопрограмму, я создаю поток, а затем выполняю рукопожатие, которое заканчивается вызовом Monitor.Wait (), который блокирует поток сопрограммы - он больше не будет работать, пока не будет разблокирован.Когда пришло время вызывать сопрограмму, я выполняю эстафетную передачу, которая заканчивается блокировкой вызывающего потока и выполнением потока сопрограммы.Такая же передача обслуживания на обратном пути.

Эти передачи обслуживания являются дорогостоящими по сравнению с другими реализациями.Если вам нужна скорость, вы захотите написать свой собственный конечный автомат и избежать всего этого переключения контекста.(Или вы захотите использовать среду исполнения с поддержкой оптоволокна - переключение оптоволокна довольно дешево.) Но если вы хотите выразительный код, я думаю, что сопрограммы имеют некоторое обещание.

0 голосов
/ 12 сентября 2018

Каналы недостающей части

Трубопроводы - недостающая часть относительно каналов в Голанге.Каналы на самом деле делают галан тик.Каналы являются основным инструментом параллелизма.Если вы используете что-то вроде сопрограммы в C #, но используете примитивы синхронизации потоков (семафор, монитор, блокировка и т. Д.), То это не то же самое.

Почти то же самое - конвейеры, но запеченныечерез

8 лет, и .Net Standard (.Net Framework / .Net Core) поддерживает конвейеры [https://blogs.msdn.microsoft.com/dotnet/2018/07/09/system-io-pipelines-high-performance-io-in-net/]. Конвейеры предпочтительнее для сетевой обработки.В настоящее время Aspcore оценивает один из 11 самых популярных запросов к пропускной способности в незашифрованном виде [https://www.techempower.com/benchmarks/#section=data-r16&hw=ph&test=plaintext].

. Microsoft рекомендует лучшие методы взаимодействия с сетевым трафиком: ожидаемые сетевые байты (IO порта завершения) должны помещать данные в конвейер,и другой поток должен читать данные из конвейера асинхронно.Многие конвейеры могут быть использованы последовательно для различных процессов в потоке байтов.В конвейере есть считыватель и курсор записи, а размер виртуального буфера приведет к тому, что обратное давление в модуле записи уменьшит ненужное использование памяти для буферизации, как правило, замедлит сетевой трафик.

Существуют некоторые критические различия между конвейерами иПерейти каналы.Трубопроводы - это не то же самое, что канал Голанга.Конвейеры предназначены для передачи изменяемых байтов, а не каналов Голанга, которые предназначены для передачи сигналов с помощью ссылок на память (включая указатели).И, наконец, не существует эквивалента select с конвейерами.

(конвейеры используют пролеты [https://adamsitnik.com/Span/],, которые существовали некоторое время назад, но теперь глубоко оптимизированы в .Net Core.производительность значительно. Поддержка ядра .Net еще больше повышает производительность, но только постепенно, поэтому использование .Net Framework прекрасно.)

Таким образом, конвейеры - это встроенный стандарт, который должен помочь заменить каналы golang в .Net, но онине совпадают, и будет много случаев, когда конвейеры не являются ответом.

Прямые реализации канала Голанга

Вы должны быть осторожны (как в случае с golang), когда сообщения, передаваемые через канал .Net, указывают на смену владельца объекта.Это то, что только программист может отследить и проверить, и если вы ошибетесь, вы получите два или более потоков, обращающихся к данным без синхронизации.

0 голосов
/ 14 апреля 2014

Вас может заинтересовать , это - библиотека, которая скрывает использование сопрограмм. Например, чтобы прочитать файл:

//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();

Эта библиотека использует один поток для запуска всех сопрограмм и позволяет вызывать задачу для действительно асинхронных операций. Например, для вызова другого метода в качестве сопрограммы (иначе говоря, для его возврата

//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;
...