Форум программистов, компьютерный форум, киберфорум
Наши страницы
C# .NET
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.75/4: Рейтинг темы: голосов - 4, средняя оценка - 4.75
jahtemg
18 / 20 / 12
Регистрация: 24.06.2016
Сообщений: 71
Завершенные тесты: 1
1

Выполнение "асинхронного" кода на нескольких ядрах

07.11.2016, 15:11. Просмотров 663. Ответов 12
Метки нет (Все метки)

Вопрос таков:

Насколько мне известно, код использующий Begin*/End* на самом деле не выполняется на нескольких ядрах, а только на одном (т.е. максимальная загрузка ЦП, если процессор имеет 4 ядра - 25%).

Хотелось бы узнать, как вы с этим боритесь. Интересно то, что если создать как минимум 4 обычных Thread, и запустить в них бесконечный цикл (разумеется, без всяких там Thread.Sleep), то загрузка ЦП будет вполне себе достигать 100%.

Речь, в основном, идет о коде сетевых приложений (серверах).
0
QA
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
07.11.2016, 15:11
Ответы с готовыми решениями:

Ошибка "Тип "MyPoint" создан из нескольких разделенных классов в одном и том же файле." при открытии дизайнера
Добрый день, формучане. У меня возникла необходимость добавления моего класса в коллекцию, для...

Ошибка "Недопустимая операция в нескольких потоках: попытка доступа к элементу управления "richTextBox1" не из того потока, в котором он был создан."
Код клиента: public partial class Form1 : Form { string IP; string Port;...

Проверка даты и времени и выполнение действий, если "время пришло"
Всем доброе время суток. Недавно заинтересовался программирование на C#, попробовал различные...

Ошибка после конвертации метода на С++ к С#: "Неявное преобразование типа "int" в "bool" невозможно"
Ошибка после преобразования метода на С++ к С#: "Неявное преобразование типа "int" в "bool"...

Ошибка CS0019: Оператор "*" не может применяться к операндам типа "decimal" и "float"
Здравствуйте! Писал приложение и наткнулся на интересную ошибку (честно говоря, я не совсем понимаю...

12
freeba
Неадекват
1317 / 1109 / 212
Регистрация: 02.04.2010
Сообщений: 2,560
Записей в блоге: 2
Завершенные тесты: 2
07.11.2016, 16:17 2
Все просто - TPL
1
jahtemg
18 / 20 / 12
Регистрация: 24.06.2016
Сообщений: 71
Завершенные тесты: 1
07.11.2016, 17:40  [ТС] 3
Спасибо, freeba, но меня больше интересует как это делается более сложными способами (как говорится, как под капотом).
0
freeba
Неадекват
1317 / 1109 / 212
Регистрация: 02.04.2010
Сообщений: 2,560
Записей в блоге: 2
Завершенные тесты: 2
07.11.2016, 17:59 4
Под капотом те же Thread из ThreadPool. В любом случае реализацию никто не мешает посмотреть - исходники открыты. По крайней мере для .NET Core.
0
07.11.2016, 17:59
insite2012
Модератор
Эксперт .NET
4890 / 3842 / 1097
Регистрация: 12.10.2013
Сообщений: 11,104
Записей в блоге: 2
07.11.2016, 17:59 5
Цитата Сообщение от jahtemg Посмотреть сообщение
(как говорится, как под капотом
Тогда уточните, что вам требуется - параллельность данных или параллельность задач. В зависимости от цели используются разные приемы.
0
jahtemg
18 / 20 / 12
Регистрация: 24.06.2016
Сообщений: 71
Завершенные тесты: 1
07.11.2016, 18:43  [ТС] 6
Конкретная, хоть и надуманая задача:

Нужно сделать сервер (эмулятор) онлайн игры (мморпг).

Есть класс, отвечающий за храниение хэндлеров пакетов (классика, ushort опкод и делегат, собственно на метод-обработчик логики).

Дело в том, что обрабатывать всё это желательно сразу на нескольких ядрах, т.к. есть некоторая возможность, что сервер будет жрать весьма значитальное количество ресурсов ЦП (разумеется, не только ЦП).

Допустим, у нас есть контекст клиента (сессия), который использует BeginReceive/EndReceive/BeginSend/EndSend. Из EndReceive (то что получено сервером от игрового клиента) мы вызываем метод в классе, который хранит делегаты на методы-обработчики, допустим,

C#
1
var handler = PacketManager.GetHandler(0x1234);
Затем, просто вызываем обработчик

C#
1
handler(myPacket);
Естественно, все обработчики будут выполнятся на одном ядре, как я и писал выше. А это значит, что если метод-обработчик весьма ресурсоемок, то результат - высокая нагрузка на 1 ядро ЦП (допустим, аж 25%), а этого, теоретически, может быть и недостаточно для поддержания геймплея без задержек.


Мои вариант:
Запустить некоторое количество потоков, из которых запускать контексты клиента. Разумеется, с синхронизацией нужно будет изрядно помахаться...

Собственно, как вы бы решили эту проблему ?
0
freeba
Неадекват
1317 / 1109 / 212
Регистрация: 02.04.2010
Сообщений: 2,560
Записей в блоге: 2
Завершенные тесты: 2
07.11.2016, 19:12 7
jahtemg, Элементарная задача. Вешаем асинхронный обработчик запросов, т.е. каждый запрос будет обрабатываться в отдельном таске, а для синхронизации хватит обычного ConcurrentDictionary. Таким образом за нагрузку на ядра будет отвечать TPL.
0
jahtemg
18 / 20 / 12
Регистрация: 24.06.2016
Сообщений: 71
Завершенные тесты: 1
07.11.2016, 19:43  [ТС] 8
Меня интересуют именно варианты решения задачи без TPL.
0
freeba
Неадекват
1317 / 1109 / 212
Регистрация: 02.04.2010
Сообщений: 2,560
Записей в блоге: 2
Завершенные тесты: 2
07.11.2016, 20:16 9
jahtemg, а какая разница? Вместо тасков можно треды создавать - принцип один.
0
jahtemg
18 / 20 / 12
Регистрация: 24.06.2016
Сообщений: 71
Завершенные тесты: 1
08.11.2016, 03:26  [ТС] 10
Собственно, мой вариант - верный ? ConcurrentDictionary для синхронизации ? Может вы имели в виду ConcurrentQueue ?
0
freeba
Неадекват
1317 / 1109 / 212
Регистрация: 02.04.2010
Сообщений: 2,560
Записей в блоге: 2
Завершенные тесты: 2
08.11.2016, 09:59 11
Цитата Сообщение от jahtemg Посмотреть сообщение
Собственно, мой вариант - верный ? ConcurrentDictionary для синхронизации ? Может вы имели в виду ConcurrentQueue ?
Эм, плохо представляю как приспособить очередь под хранение сессии.

Набросал пример с асинхронным сервером, разница между использованием тасков и потоков для обработки - одна строка.
C#
1
2
Task.Run(() => Worker(socket)); //Таски
ThreadPool.QueueUserWorkItem(new WaitCallback(WorkerThread), socket); //Потоки
Производительность примерно одинаковая, но микрософт уже давно рекомендует использовать TPL.

PS: Это тестовый пример - в боевых условиях, сокеты не переподключаются на каждый запрос.
0
Вложения
Тип файла: 7z CF.Test.7z (36.3 Кб, 5 просмотров)
jahtemg
18 / 20 / 12
Регистрация: 24.06.2016
Сообщений: 71
Завершенные тесты: 1
08.11.2016, 11:58  [ТС] 12
Для хранения и управления сессиями я обычно делаю отдельный класс.
Спасибо за код... Но я имел в виду то, что я думаю что очень желательно ограничивать количество этих самых потоков в которых будут запускаться воркеры сессии (в моем случае, просто вызов session.Start()).

Про ConcurrentQueue - я имел в виду то, что можно принятые подключения добавлять в очередь (SessionWorkItem) и затем оттуда вытягивать и запускать в одном из предварительно созданных потоках (вполне типичный producer-consumer?).
0
freeba
Неадекват
1317 / 1109 / 212
Регистрация: 02.04.2010
Сообщений: 2,560
Записей в блоге: 2
Завершенные тесты: 2
08.11.2016, 13:01 13
А зачем? На MSDN рекомендуют не сильно увлекаться ручной установкой ограничений.

Пул потоков предоставляет новые рабочие потоки или потоки завершения ввода-вывода по запросу, пока не будет достигнут минимум для каждой категории. При достижении минимума пул потоков может создавать дополнительные потоки в этой категории или ожидать завершения некоторых задач. Начиная с .NET Framework 4 пул потоков создает и уничтожает рабочие потоки в целях оптимизации пропускной способности, которая определяется как количество задач, завершаемых за единицу времени. Слишком малое количество потоков может препятствовать оптимальному использованию доступных ресурсов, тогда как слишком большое их количество может усиливать конкуренцию за ресурсы.

Можно использовать SetMinThreads метод для увеличения минимального количества потоков. Однако необоснованное увеличение этих значений может привести к снижению производительности. Если одновременно запускается слишком много задач, все они могут выполняться слишком медленно. В большинстве случаев пул потоков работает наилучшим образом, если он использует собственный алгоритм выделения потоков.
Цитата Сообщение от jahtemg Посмотреть сообщение
Про ConcurrentQueue - я имел в виду то, что можно принятые подключения добавлять в очередь (SessionWorkItem) и затем оттуда вытягивать и запускать в одном из предварительно созданных потоках (вполне типичный producer-consumer?).
Принятые подключения проще заносить в список и вешать event отслеживающий поступление данных. Как только данные поступили - их сразу на обработку в пул потоков. Вроде так проще.
0
08.11.2016, 13:01
Answers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
08.11.2016, 13:01

Ожидание выполнение асинхронного метода
Всем привет. Помогите разобраться с асинхронным программированием. Пишу приложение для windows...

Почему первый вариант дает исключение "Недопустимая операция в нескольких потоках", а второй нет?
есть два варианта кода: Task.Run(() => { ...

Переопределить операции "+" "=" "-" для экземпляров моего класса
Добрый день. Мне нужно переопределить операции "+" "=" "-" для экземпляров моего класса. Я вижу это...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
13
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru