остальных потоков? То есть друг друга.
Если в целом процесс, сумма потоков и 1% мощности еле использует.🤔
Может. Вы лучше или код покажите, или разбирайтесь почему у вас потоки не нагружены. Просто сделать 1000 потоков не выход - накладные расходы будут бешеные.
Есть например накладные ресурсы, за железку процессорного времени, за синхронизацию, за выделение потоков если ты создаёшь их, и тд
Есть 1500 устройств, нужно каждые несколько сек опрашивать, и ожидать ответа. Сейчас дошло до того, что в каждом потоке сначала идёт Sleep, потом ожидание Slim семафора. Вычисления небольшие, но ожидания долгие. И по ощущениям, это садит другие потоки из Пула. Возможно, по факту сверху приложения есть системный цикл, переключающий все потоки? Но почему это медленно, ведь нагрузки на ЦП нет.🤔
https://ru.bmstu.wiki/%D0%9F%D0%B5%D1%80%D0%B5%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BD%D1%82%D0%B5%D0%BA%D1%81%D1%82%D0%B0
Вы там что, рожаете 1500 потоков? Даже не тасков?
Вообще потоков 3000. Но они 1 раз родились, а потом работают. Код похож на while(Active) { Work();//тоже может ждать долго Sleep(1000); }
В идеале - перепишите всё (в том числе этот Work()) на таски. Незачем рожать 3к потоков, чтобы они потом спали, если можно обойтись парочкой, что есть в системном тредпуле
Но таски либо будут создавать каждый раз кучу новых потоков, что ещё большая нагрузка. Либо будут работать в пуле, а в нём почему то не выделяется более 30 потоков, из-за чего всё приложение зависает. Так как стоит очередь в 3000 запросов.
Во первых, размер пул регулируется. Во вторых - вы таки почитайте как это работает. Вы не пробовали, а уже критикуете.
Так я с этого уже ушёл. Использовал Threading.Timer. Task.Run тоже использовал.
Что представляет собой Work? Это IO или вычисления?
Это отправка запроса на устройство, и ожидание ответа.
То есть вы даже не вычисляете потом нчиего по этому ответу? Вам тасков хватит с головой. Только таски должны быть ВЕЗДЕ. Начиная от этого цикла, и заканчивая отправкой/получением ответа от устройства. Почти всё IO в дотнете умеет в таски, всякие SerialPort не исключение
Так это как раз на таски и асинхронность
Но как Таски это обработают, если по факту нужно обрабатывать одновременно много долгих запросов?
Почитайте документацию, ещё раз. Они просто не будут напрасно держать поток, когда его можно не держать. То есть во время этого слипа, например, таска отпустит поток заниматься другими делами и продолжится позже, по истечению "слипа". И на ожидании ответа от устройства тоже.
у устройств есть уникальные id?
Попробую изучить🥺 Понимаю что ожиданий не должно быть. Но не знаю как
Ну вот асинхронное программирование вам ответ. Как раз проблему ожиданий и решает (в том числе)
а что мешает сразу заслать всем команду и ловить ответы и писать их в таблицу по мере ответов id, время, ответ и играйте в эту таблицу как хотите
Что значит сразу всем? Нужно регулярно, каждые 5 сек повторять команду. Кто будет инициатором этой команды?
а сейчас кто шлет? тот пусть и шлет раз в 5 сек команду, время отправки есть и 2 поток пусть слушает ответы и пишет время ответа и если на ответ будет уходить более 5 сек то тогда что делаем?
Пока ответ не придёт, следующая отправка не начнётся. Период ожидания ждётся после получения ответа. А отправить второй раз, не дождавшись ответа, нельзя.
тогда о каких 5 сек речь? кто то за 1 сек ответил, кто то за 10 и ситуация, что ответ вообще не пришел, то мы его будем ждать вечность? тут или устанавливать период за который будет гарантированный ответ или что мешает 2 и последующие отправки делать только если получен ответ от устройства
тебе нужны не таймеры а какая-то очередь
Так последующие отправки и так идут через 5 сек после получения ответа.
тогда у тебя там полнейший рандом, и 5 сек паузы ни на что не влияют
Всмысле ни на что? Всмысле, они не должны понижать производительность?
понизить то чего нет, это фантастика цель иметь срез раз в 5 сек, ответ гарантировано менее 3 сек вариант с тем что ответ не прийдет как реализован?
У семафора есть Wait(Timeout). Но это не важно ведь. это другой вопрос.
Попробуй переделать с sleep на таски и асинхронность, это получится так, что потоки будут брать работу, только тогда, когда есть чем заниматься, и в момент sleep они будут заниматься другими вещами. И может случится так что тебе нужно не 1000 потоков, а 10
Не знаю почему, но использование тасков Стабильно создаёт Дедлок всего приложения. А если то же самое запускать в отдельных потоках, то всё отлично. Это выглядит ровно так, что создав очередь из ожидающих 10 потоков, он не создаёт новые, а ждёт эти. И так как их 1500*2 сек, получается будет ждать каждого тика около 15 мин... Но почему.... Он же должен создавать сколько угодно новых потоков, если имеющиеся заняты.
Читай документацию: Когда не следует использовать потоки из пула потоков: Имеются задачи, которые приводят к блокировке потока на длительное время. Для пула потоков определено максимальное количество потоков, поэтому большое число заблокированных потоков в пуле может препятствовать запуску задач. Именно это и происходит. Так что всё же Таски это не твой вариант, плохой совет.
Я запутался. Если происходит блокировка потоков ожиданием, то почему таски — это плохой совет? Наоборот же, блокировать потоки — это плохо, поэтому таски — хороший совет.
Так полюбому выполняющаяся задача блокирующая. Состоящая из синхронной отправки и ожидания ответа. И таких задач много...
Что значит по-любому? Если она на цпу что-то считает, то да. А если она отправила запрос на диск/сеть и ждёт ответа, то что мешает её сделать асинхронной?
Код покажите. С высокой вероятностью, вы не поняли суть тасков и у вас где-то .GetResult или .Result вылезает
И этот совет актуален тогда, когда у вас нет контроля над всем кодом в цепочке и у вас нет никакого выхода, кроме как блокировать поток. Но у вас есть контроль, вам не нужно блокироваться на IO
Найн, Считайте, что запускается Task.Factory.StartNew(Thread.Sleep(3000)), TaskCreationOptions.LongRunning);
И что мешает заменить на Task.Delay(3000)?
А что это изменит? Это для примера, как блокирующая операция. Она может быть любая, как сложное вычисление, так и ожидание большого количества данных от разных микросервисов.
Ожидание станет неблокирующим.
То есть метод пойдёт работать дальше?
Да, точно так же, как и в случае Task.Factory.StartNew(Thread.Sleep(3000)), TaskCreationOptions.LongRunning); :)
Блокирующий IO - это поставить печься пирог, сесть перед печкой и ничего не делать, пока оно не испечется. Асинхронный IO - это поставить печься пирог и уйти заниматься своими делами, пока он не допечется. Делая Sleep или пользуясь чем-то блокирующим не-асинхронным, вы сидите перед печкой и смотрите на пирог. Не надо так так. Пользуйтесь асинхронными версиями с await-ами. Через что общаетесь с устройством? Http/tcp? SerialPort?
Через TcpSocket, у себя я использую BeginSend/BeginRead. Но тут такого нет.
Метод отправки запроса и получения ответа может, буквально, выглядить вот так await WriteAsync(/*arguments*/); var response = new byte[10000]; await Read(/* arguments */); //Делаем что надо с response
И пока будет ждаться Await Read, другие потоки будут продолжать работать? Но почему они сейчас не работают?
Да. Ещё раз спрошу - вы точно читали доку? Потому что там написано это, и не только
Так бывает, если у тебя код в тасках очень стрёмный.
Например, кто-то кого-то синхронно ожидает.
Необходимо пояснение. Почему это ожидание обязательно блокирующее? Это плохая идея — писать такой код, и писать его на тасках.
Бже какой срам, тебе должно быть стыдно.
Но в конце его уже не было, блокировки выключил. Выполнялся быстро, но очень много вычислений. И всё равно забивали пул.
Даже не блокирующий занимает все Таски... 😔 Либо я что то упускаю.🧐 А вообще, блокировка нужна в том числе для того, что бы новая команда не ушла до возврата предыдущей.
Для этого не нужна блокировка.
Просто "забей весь пул", а потом посмотри стеки потоков в нём, и будет видно, в чём проблема.
А как смотреть стеки потоков? Просто через просмотр потоков?🥺 Где показаны имена и состояние. Тут основная проблема, что логика была написана не мной, раньше вообще по 2 потока и таймеру создавалось для каждого устройства. Но их стало очень много. Теперь я унизил до 2 потоков на все устройства, и работу действий в пуле. Он постоянно создаёт и убивает потоки, а не использует одни и те же. Что всё же не достаточно эффективно.
Как смотреть стеки потоков? Через просмотр потоков. А как реализовать просмотр потоков? Глядя на стеки потоков!
Стыдиться или от того что написал?)
Обсуждают сегодня