Здравствуйте, netch80, Вы писали:
N>Здравствуйте, Serginio1, Вы писали:
N>>>Эээ... после выполнения захвата лока (речь об этом, да?) как раз лучше не отдавать управление, а сделать максимум действий во время захвата. А до него — если лок занят — то раз ждём, надо оповестить шедулер о том, что есть отличный повод запустить другую задачу.
S>> Я как раз про то, что если lock короткий, то не стоит передавать управление на другой поток.
N>Так передавать когда? До него или после? В случае удачного захвата или неудачного?
При удачном захвате стоит дождаться освобождения, что бы другие потоки могли воспользоваться свободным монитором
N>Да, проблема есть (хотя про "основной источник проблем с производительностью" тут загнули, случай он разный бывает). Но вы никак от этого не избавитесь ни шедулингом, ни переходом на await, пока вам потребуется ровно та же синхронизация.
Я тебе привел пример аналога LockAsync. Поток ничего не ждет, а по событию запускается из пула потоков. Вот пример асинхронной очереди
AsyncProducerConsumerCollection
Просто привел пример с использованием ValueTask вместо Task
S>>>>Для примера в эпоху до async/await поток ждет выполнения асинхронной операции.
S>>>>async/await берет на себя сохранения данных внутри класса (стек не нужен) и строит автомат и тот же поток который выполнял данную задачу, запускает другую. Нет никаких переключений.
N>>>А что, вот эти все "берёт на себя сохранения данных внутри класса" и аналогичное восстановление, по-вашему, не переключение? А что оно такое тогда?
S>> Там нет восстаеовления ибо данные хранятся в куче.
N>Если так, то та же цена реально размазана по остальной работе: с данными в куче всегда дороже работать, чем с данными на стеке или тем более в регистрах: аллокация, GC где-то после, разыменование указателей (спрятанных в ссылках дотнетов), заметно худшее кэширование, потому что в стеке данные сидят плотно, и наверняка ещё и пара сотен байт вершины стека в кэшах процессора.
Угу который при переходе на новый поток сбрасывается.
У меня вопрос если все так прекрасно с переключением потоков, то зачем делают все, что бы отказаться от потоков и использовать очередь потоков?
N>Не может быть квадратных кругов и круглых квадратов. Если с данными надо работать, то для этого нужен доступ к ним. Если нужен доступ, их надо прочитать из памяти и записать в память, с соответствующей ценой. Я бы предпочёл, чтобы компиляция хранила максимум в регистрах процессора, а что не помещается — максимум на стеке: не будет дурных потерь скорости. А если при этом само переключение формально дороже — тоже не страшно, записать компактно пару сотен байт и потом прочитать — эффективнее, чем размазывать их по десяткам кэш-строк.
Ну класс если используешь Value типы тоже будут компактно расположены, а не валуе типы что на стеке, что не на нем все равно в куче
N>>>С точки зрения логики управления выполнением — именно для тех случаев, когда шедулер узнал, что ответа для await сейчас нет и надо дать кому-то управление.
N>>>С точки зрения исходного кода — чтобы писать его максимально линейно.
S>> Линейно то и раньше писали, только внутри были всякие евенты и остановка потока.
N>Я говорю про варианты без такой остановки (если переключение, то оно минимально видно в ОС).
N>>>Переключений в нём, в идеале, столько же, сколько в аналогичном коде на коллбэках или промисах.
S>> Ну в итоге то колбеки запускаются из пула потоков.
N>Ну это уже дотнетовая специфика и не обязательно так везде. И это не обязательно удобно. Я во многих случаях хотел бы ограничить такое явно только тем же тредом, который вызвал исходную операцию.
S>>>>Ну и замена всяких Lock на ManualResetValueTaskSource
S>>>>http://rsdn.org/forum/dotnet/8030645.1Автор: Serginio1
Дата: 16.06.21
S>>>> https://stackoverflow.com/questions/66387225/awaiting-a-single-net-event-with-a-valuetask
N>>>Я что-то не могу это раскурить. Какой смысл в его применении?
S>> Смысл в том, что замена lock на lockAsync. То есть нет никакого ожидания потока
N>Если оно сделано умно (с попытками захвата до переключения) — отлично.
S>>>>Конечно зависит от длительности задач, но если задачи непродолжительные, то пул потоков может и не переключаться а выполнять очередь заданий.
N>>>Тоже не понимаю, при чём тут эта реплика.
S>> В том, что с использованием пула потоков сводит к минимуму переключение потоков
N>Так не от желания конкретной задачи тут зависит, а от того, будет ли тот лок свободен.
Вот ссылочка из предыдущего сообщения
AsyncSemaphore
Если lock занят то возвращается Task и ожидается пока не вызовется SetResult
Никакой остановки потока нет. Да внутри конечно есть короткий lock который нужно заменять на SpinLock