• Async/await в C#: концепция, внутреннее устройство, полезные приемы
    0
    Зависит от конкретной задачи. В ряде случаев внутри вызванной вами асинхронной операции работа будет отправлена на выполнение в пул потоков, т.е. над ней будет работать отдельный поток. В случаях с операциями ввода-вывода действия по чтению-записи могут быть делегированы на контроллер устройства, не занимая лишних потоков.
    Поток выполняется как обычно, далее вызывает асинхронную операцию. Она начинает выполняться одним из вышеперечисленных способов. В вызвавший ее поток возвращается Task — индикатор завершенности этой операции. Далее поток просто засыпает на 1.7с. после того, как он просыпается он встречает await и проверяет, завершена ли операция, представленная тем Task. В данном случае да, завершена. Поэтому мы можем продолжать выполнение в этом же потоке как обычно.
  • Async/await в C#: концепция, внутреннее устройство, полезные приемы
    0
    Возвращаемое значение не влияет на асинхронность. Если есть метод, возвращающий void, то его асинхронность зависит от наличия внутри вызовов асинхронных операций с применением await для ожидания их завершения.
  • Async/await в C#: концепция, внутреннее устройство, полезные приемы
    0
    Ну в теории да, если кто-то пишет странные методы вроде этого (не дожидаясь запущенных задач).
        public static async Task Main()
        {
            await BlackBox.Method1Async();
            await BlackBox.Method2Async();
            Thread.Sleep(3000);
        }
    
        public static class BlackBox
        {
            public static async Task Method1Async()
            {
                Console.WriteLine($"Method1Async 1");
                await Task.Delay(500);
                Console.WriteLine($"Method1Async 2");
                Task.Run(async () => // не дождались
                {
                    await Task.Delay(500);
                    Console.WriteLine($"Method1Async 3");
                });
            }
    
            public static async Task Method2Async()
            {
                Console.WriteLine($"Method2Async 1");
                await Task.Delay(500);
                Console.WriteLine($"Method2Async 2");
            }
        }
    

    Но в нормальных ситуациях — нет, во всяком случае мне на ум ничего не приходит.
    Одна задача многими потоками: обычный асинхронный метод, который выполняется реально асинхронно. Его продолжение может быть выполнено на потоке, отличном от первоначального. В то же время код, который вызывает этот метод не думает об этом. С его стороны — это логически едина язадача, над которой поработало несколько потоков. Примерно это я имел в виду
  • Async/await в C#: концепция, внутреннее устройство, полезные приемы
    0
    Под задаче подразумевал те действия, состояние которых отражено в объекте Task. Как-то так. Поток — просто поток выполнения, насколько я помню, нигде не опирался на конкретные детали System.Threading.Thread.
    Если есть вопросы конкретно по материалу, задавайте. Мне тогда будет легче пояснить
  • Async/await в C#: концепция, внутреннее устройство, полезные приемы
    0
    Ну кроме редких случаев да, вы правы. После первого же await уже выполнение будет возложено на пул. Разве что вы долго и упорно что-то делаете и в конце асинхронно, допустим, отсылаете результаты по сети. Тогда смысл есть.
  • Async/await в C#: концепция, внутреннее устройство, полезные приемы
    0
    Пример данного поведения приведен в главе с машиной состояний и ее кодом. Далее следует маленький пример с Thread.Sleep. Там мы запускаем задачу, которая выполняется секунду, далее засыпает на 1.7 секунды и ожидаем запущенную задачу. Т.к. мы спали достаточно долго и запущенная задача уже выполнилась, то нет смысла присоединять продолжения и т.д. Мы просто продолжаем выполнение, т.к. результат к этому моменту доступен. Если вы измените засыпание с 1.7 на что-нибудь меньше секунды (время за которое выполняется задача), то продолжение выполнится в другом потоке, т.е. на консоль будут выведены разные числа.
    В реальном мире так обычно бывает, когда операция выполняется в ряде случаев сразу, а в остальных уже дольше. Пример тому файлы (которые я описывал) — если мы записываем достаточно малый (влезающий в буфер) объем данных — то продолжение никуда не присоединяется, т.к. операция завершается синхронно. Если мы пишем большой объем данных, то операция выполняется асинхронно и продолжение будет присоединено к еще не завершенной задаче. Вот еще вам пример с файлами
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
    int bufferSize = 4096; // менять здесь. Можно менять или размер буфера, или данных.
    int dataSize = 2000; //Если bufferSize > dataSize, то продолжение выполняется синхронно в том же потоке. В противном случае - в другом
    using (var fs = new FileStream("aswt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, bufferSize, true))
    {
            var ar = new byte[dataSize];
            new Random().NextBytes(ar);
            await fs.WriteAsync(ar);
    }
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
    
  • Async/await в C#: концепция, внутреннее устройство, полезные приемы
    0
    Видимо, вы не так поняли. Имелось в виду, что помечать метод async, если в нем нет await не имеет смысла. Метод преобразуется в заглушку, будет сгенерирована стейт-машина, но это будет сделано зря, т.к. нигде не будет присоединено продолжений. Компилятор при этом выдаст предупреждение «В данном асинхронном методе отсутствуют операторы await ...».
  • System.IO.Pipelines — малоизвестный инструмент для любителей высокой производительности
    +1
    Конечно, спасибо за исправление, я что-то тупанул и везде lang=«csharp» поставил
  • Индексаторы в C# под капотом: индексируем лучше Доу-Джонса
    0

    Что будет со второй проверки надо проверять. Имеет место оптимизация, при которой в простейших циклах эта проверка убирается, если из цикла видно, что мы не выйдем за границы.
    А вот будет ли понятно это для jit, это уже вопрос. Надо будет проверить на досуге.

  • Индексаторы в C# под капотом: индексируем лучше Доу-Джонса
    0

    На то оно и предположение было. Спасибо за уточнение, надо будет поправить в статье.

  • Что происходит за кулисами С#: основы работы со стеком
    +1
    Я вроде писал где-то, что все примеры для 32 бит. Сейчас перечитаю, мб в дисклеймер внесу. Но вы правы, конечно.
    Справедливости ради, в .NET Core еще и структура таблицы методов поменялась. Общая схема работы осталась, но конкретные смещения уже не те.
  • Ломаем фундаментальные основы C#: выделение памяти под ссылочный тип на стеке
    0

    Да, согласен. Как доберусь до компутатора, сделаю где-нибудь пометку

  • Ломаем фундаментальные основы C#: выделение памяти под ссылочный тип на стеке
    0
    Обдумывал и такой вариант. Теоретически, можно даже просто переменных нужное количество завести, но это не динамически. Тут, конечно, мой косяк, поленился написать нормальный метод main. Просто суть в том, что так я могу выделить на стеке память под любой размер класса (то бишь для любого класса). А в варианте со структурами (или кучей переменных) придется заводить их на каждый случай жизни, никакой динамики.
    А так да, если подобрать нужную структуру, то должно получиться!
  • Ломаем фундаментальные основы C#: выделение памяти под ссылочный тип на стеке
    +6
    Как я написал в самом начале: «Это просто расширение границ, в которых воспринимается язык программирования». То есть интересный пример, показывающий некоторые аспекты внутреннего устройства
  • Что происходит за кулисами С#: основы работы со стеком
    0
    Как я выше упоминал, я использовал тот ресурс для получения кода Ассемблера. А они видимо решили использовать 32 бита. В этом плане я даже и согласен с ними. Это классика — 32 бита на ссылку и прочее.
    Когда я использовал windbg я наблюдал 64 битный код.
    Как я знаю, от Int32 он не зависит, как известно, int — просто элиас для Int32. А число 32 в названии Int32 означает, что мы используем 32 бита для представления числа. Ведь Int64 (он же long) доступен не только на 64 битной платформе. Мы, как разработчики, не должны об этом знать. Об этом должна заботиться среда, а мы просто работаем с 32 или 64 битным числом. И т.к. мы кроссплатформенны, это не наше дело, свалим это на плечи JIT'a
  • Что происходит за кулисами С#: основы работы со стеком
    0

    В первых своих исследованиях я использовал winDbg, но там надо учитывать, что для начала надо дождаться, пока функция вызовется хоть раз (до этого JIT ее просто не скомпелит) или предкомпелить. Разумеется, windbg очень мощный, но для таких примеров слишком громоздкий.
    И не так давно один добрый человек мне подсказал сайт https://sharplab.io. Я сверял с windbg (к нему было доверие), все совпадает. Так что для небольших примеров пользуюсь им.

  • Что происходит за кулисами С#: основы работы со стеком
    +1
    Пример чисто для фана. Думал о его применимости (возможно, для повышения привилегий, где вызов стандартных функций принимает интерфейс/делегат), но не нашел. Да и не особо искал, если честно, не ставил это целью. Ну а еще люблю понимать, как оно все там происходит.
  • Не уважаю инкапсуляцию, или использование таблицы методов другого типа для быстрого вызова приватных методов
    0

    Виртуальные методы вызываются по смещению в таблице методов. На этом и основана вся эта статья.
    И благодаря этому при вызове метода CompareTo на CustomClass, вызывается метод строки. Это же и объясняет поведение при замене виртуального свойства на клон Equals. Заглушка действительно генерируется(для всех методов), но ради последующей компиляции JITом(имеет единсвеннную инструкцию на тригеринг JITа), которая потом затирается и изменяется на jmp в нужное место памяти, где расположен скомпилированный метод.

  • Не уважаю инкапсуляцию, или использование таблицы методов другого типа для быстрого вызова приватных методов
    0
    Могу высказать текущую гипотезу. Во-первых: при проецировании на Equals, параметр внуть метода строки передается null, скорее всего это связано с тем, что данный параметр передается через регистры, и значение в регистре при вызове метода соответсвет null. Второе — при этом метод возвращает false, то бишь нули (память). Которые интерпритируются как null в случае ссылочных типов, как 0 в случае int и тд.
    Это звучит довольно дико для шарпа. И я не утвержаю, а лишь предполагаю. Возможно, это послужит толчком для дальнейших исследований (моих или ваших).
  • Не уважаю инкапсуляцию, или использование таблицы методов другого типа для быстрого вызова приватных методов
    0
    На самом деле вопрос действительно сложный.
    Весь этот пример основан лишь на смещениях. Например, в смещении метода CompareTo я уверен, он совпадает с моим CompareTo. Однако свойство, когда идет первым, проецируется на другой метод. В моем случае это Equals. Попробуйте сделать вместо данного свойства аналог метода Equals. А далее попробуйте передать такую-же строку («4564» в моем случае), или другую. Результаты будут ожидаемыми для метода Equals (true и false соотвественно).
    Причина же по которой возвращаемое значение становится null (строго говоря, дефолтным, для int будет 0) мною не разгадана. Если вам интересно, попробуйте подебажить в dnSpy, там можно своими глазами увидеть, в какой метод переходит выполнение. Порядок методов в таблице методов не тривиален. Есть правила, по которым располагаются методы, но я предпочитаю видеть точно.
  • Не уважаю инкапсуляцию, или использование таблицы методов другого типа для быстрого вызова приватных методов
    +1

    Не пробовал и, если честно, не хотелось бы.
    Я думал о возможностях использования, но возможность отстрела ноги слишком велика. Представляет интерес скорее с научной точки зрения.

  • Не уважаю инкапсуляцию, или использование таблицы методов другого типа для быстрого вызова приватных методов
    0
    Согласен, целью рефлексии, разумеется, не является нарушение инкапсуляции. Однако это не отменяет того факта, что с ее помощью (при желании), нарушение инкапсуляции легче некуда.