Pull to refresh

Comments 30

Какой-то список кринжа, боянов, ещё и с неправильными ответами...

Что стоит знать об уровне блога
Тем временем на главной станице сайта
Тем временем на главной станице сайта

Такие статьи не должны писать копирайтеры. Нужно хоть какое-то представление о теме разговора иметь, а не просто надергать вопросы их десятков таких же топов, гуляющих по интернету уже который год.

Если бы копирайтер. Судя по другим подборкам, это пишет тупо ИИ. Причем на любую тему - хочешь тебе Go, хочешь - Питон, хочешь - SQL, хочешь - вот Шарпы. Причем пишет с нескольких аккаунтов.

По факту это рак, который добивает и так уже почти бессмысленный Хабр. Но всем, как обычно, пох.

Но у него в канале достаточно подписчиков, чтобы апать статью, так что она все равно будет в плюсах

Тихо, тихо, щас ChatGPT проиндексирует статью, и у нас будущем все еще будет работа.

Асинхронные методы выполняются в отдельных потоках

Не буду говорить за все остальное, но async/await блок провальный. Буквально с первой же фразы - асинхронность, это чуть ли не противоположность многопоточности, кроме пары исключений.

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

но async/await блок провальный

но очень интересный, очень хорошо оформлено-изложено, я теперь хорошо понимаю что мне хотели внушить в коментариях к моей недавней статье

и с этим можно работать и разобрать каждое противоречие в изложенном материале по полочкам. Я скопировал себе для работы.

Окинув взглядом этот пост еще раз я понял - бездушная копипаста. Это даже не GPT, сурс.

но мне под моей статьей писали явно не копипастом, писали с эмоциями, но по смыслу писали примерно то же самое.

Теперь ссылку посмотрел, действительно копи-паст, классно :) !

Я читал вашу статью и понял что вы хотели донести, но как лично я вижу это:
Есть Клири (надеюсь правильная транслитерация) и есть, допустим, Metanit. Первый говорит что асинхронность не создает потоки вообще никогда (хотя на самом деле, если прокликать его сайт, то можно наткнуться и на await Task.Run() в частных случаях), а второй говорит полностью обратное.
И тут есть момент: лучше человеку вбить в голову что асинхронность != потоки, чем вбить обратное. Объясню на примере: я сам учебный план строил по Metanit, просто я разбавлял его поверхностность конкретикой с msdn и пр., и я помню, что асинхронность у него идет сразу после TPL. Таким образом, учитывая, что его статьи написаны для начинающих, и написаны слишком уж коротко, получается такой вопрос: а зачем оно надо, когда задачу можно решать синхронно-мультипоточно. Если же человеку сказать обратное, то у него как минимум возникнет вопрос "а как тогда это работает", и дальше он уже сам нагуглит и про контексты и про configureAwait и про UI/ASP потоки.
Конечно, лучше всего написать простыню текста, объясняющего все азы async/await, но это работа для Майкрософт (хотя, надо признать, их async/await написан вполне человеческим языком, да и половина статей там за авторством Клири).

Первый говорит что асинхронность не создает потоки вообще никогда ..., а второй говорит полностью обратное.

просто вот с этого надо начинать:

определения синхронного и асинхронного методов, которое дал Стивен Тоуб (например):

this method is “synchronous” because a caller will not be able to do anything else until this whole operation completes and control is returned back to the caller

 this method is “asynchronous” because control is expected to be returned back to its caller very quickly and possibly before the work associated with the whole operation has completed

здесь действительно про потоки ничего нет, казалось бы.

Я напишу наверно в ближайшем будущем.

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

Не буду говорить за все остальное, но async/await блок провальный.

Он вообще содержит не только неточности, но и явную ошибку:

async Task<Model> GetModel(Func<Model, bool> condition)
{
   var model = await Task.Run(dbContext.Models.FirstOrDefault(condition);)
   return model;
}    

Этот код выше будет работать, но асинхронности здесь не будет. Потоки освобождаться не будут. Наоборот, текущий поток должен дождаться выполнения Task, которая обращается к БД. В момент когда выполнение доходит до await выполнение прерывается и текущий поток освобождается. Но код внутри Task не является асинхронным, и он заморозит поток, пока не получит ответ от БД.

В цитате - чушь. В реальности await Task.Run тут же (если созданная задача не завершилась сразу) вернет управление в метод вызвавший GetModel() и тот может выполнить что-то ещё, пока ему не потребуется результат от GetModel(). А до того текущий поток отнюдь не прерывается, а продолжает выполнять код вызывающего метода.

Что там дальше в статье - не читал: этого мне достаточно, чтобы сделать вывод.

PS А еще - по синглетону: автору, похоже, невдомек, что сервис-Singleton - это типичный обитатель DI-контейнера, без которого, например, приложение на ASP.NET Core просто не может обойтись.

Вызов dbContext.Models.FirstOrDefault синхронный же. По моему верно написано - текущий поток будет отпущен, а вот поток внутри Task.Run - синхронно ждёт.

Ну и пусть он ждет синхронно. Но для того, кто вызывает GetModel(), асинхронность будет - он в это время может сделать что-то полезное? Или я как-то неправильно понимаю слово "асинхронность"? Ну, тогда ладно: это - всего лишь слово.

Но глаавное "не то" написано не здесь, а вот тут

Потоки освобождаться не будут. Наоборот, текущий поток должен дождаться выполнения Task, которая обращается к БД.

Вот это - уже не всего лишь слово, а неправильное описание того, что происходит: текущий поток вполне себе продолжает выполняться дальше: возвращается в код, вызвавший метод GetModel() и может там сделать что-то ещё, пока где-нибудь не понадобится узнать результат этого синхроннно ожидающего выполнения обращения к EF. А если и там, где надо узнать, тоже await, и тоже - первый сработавший, то поток вернется ещё на один уровень вложенности выше и т.д. - пока не упрется в Wait или не вернется, к примеру (допустим, что никакого контекста синхронизации у нас нет), в пул, выполнив всю доступную на данный момент работу.

К вопросу с синглетоном: вообще-то его предложили для С++, поскольку там не определён порядок инициализации статических переменных. В C# достаточно создать статический экземпляр класса, и со всем остальным котом можно не заморачиваться.

Абстрактные классы нужны для того, чтобы выделять общий функционал от нескольких классов в обособленный класс. От этого отдельного класса потом можно унаследовать либо просто сигнатуру функционала, либо вместе с реализацией.

Класс обязательно нужно объявлять как абстрактный, когда он содержит абстрактные члены.

Порадовало. Особенно последнее предложение. Масло-маслинное.

Класс абстрактный потому что он содержит абстрактные методы. А не наоборот.

Правильный вопрос: зачем вам абстрактные классы если есть интерфейсы. Ну или в чем отличие интерйфецсов от абс тактных классов.

Не уверен, что правильно понял.

Можно сделать абстрактный класс без абстрактных методов и свойств.

Такой класс нельзя будет создавать (через new по крайней мере), что потребует явно объявить наследник для использования.

А вот если в обычный класс воткнуть абстрактный метод - да, класс придётся тоже объявить абстрактным.

Так что вроде в целом текст логически верный.

интерфейсы элемент компонентного программирования , в ооп они не нужны )

Интерфейс - реализация фундаментального понятия ООП и в абстрактном смысле включены в каждый класс.
Наследование реализаций - вот от чего можно было бы избавиться.

толсто, или наверно очень тонко)

Ну хз
Есть интерфейс в абстрактном смысле - это то, как с объектом можно взаимодействовать. И это буквально список публичных функций и полей объекта.
В этом смысле интерфейсы есть в большинстве ЯП

И есть интерфейсы в техническом смысле - интерфейсы в C# и Java, протоколы в obj-c, трейты в Rust, ленивая типизация в Go и т.д.

Интерфейсы можно наследовать и использовать в полиморфизме (когда один и тот же метод работает по разному в разных объектах)

В этом смысле к абстрактным интерфейсам и наследованию интерфейсов претензий нет.

Проблемы начинаются, когда наследуется не только интерфейс.

Значимые типы (value type) хранятся в стеке

ага, конечно...

class SomeClass
{
  int someField; // <--- int значимый? someField хранится в стеке?
}

и да, "локальные переменные value type хранятся в стеке" - тоже неправильный ответ (контрпример - замыкание).

---

неуправляемые ресурсы памяти

вместо того, что б создавать пользовательские финализаторы

не "памяти", и не "вместо". и собеседующий будет рад услышать про Safe handles.

Локальные value type действительно храняться на стеке (не надо путать людей), а в коде с замыканием значение локальной переменной будет копироваться в поле экземпляра класса-обертки функции замыкания

Значение переменной в случае замыкания не просто "будет копироваться в поле экземпляра класса". Оно будет в нем храниться, потому что если вы из замыкания поменяете значение - оно поменяется и в самой переменной.

Локальные переменные reference type тоже хранятся в стеке. Простейший пример:

void SomeMethod()
{
  string some = null; // <--- локальная переменная, reference type
  //...
}

Вы же не станете утверждать, что этот null хранится в куче? :) Если сомневаетесь - проверьте тем же ildasm

C#

string some = null;
int someInt = 42;

IL
.method public hidebysig 
    instance void SomeMethod () cil managed 
{
    // Method begins at RVA 0x2050
    // Code size 7 (0x7)
    .maxstack 1
    .locals init (
        [0] string some,
        [1] int32 someInt
    )

    IL_0000: nop
    IL_0001: ldnull
    IL_0002: stloc.0
    IL_0003: ldc.i4.s 42
    IL_0005: stloc.1
    IL_0006: ret
} // end of method C::SomeMethod

IMHO, очевидно - две локальных переменных, одна value type, вторая reference type - хранятся абсолютно одинаково (в local variable list, а не в стеке). А через стек - только значения в них закидываются.

Можно, конечно, утверждать, что после компиляции jit-ом, значения value type будут хранится в стеке (в чем-то, на что указывает ESP) - но и это не будет соответствовать реальности, т.к. для небольших методов jit запихнет значения в регистры и обойдется совсем без стека (опять же, одинаково и для value type, и для reference type).

Стек - это детали реализации. На уровне C# стека нет (а StackOverflow есть :), на уровне IL стек есть, но переменные лежат не в нем, а в locals, а после JIT они могут лежать в стеке, а могут и не лежать. Причем как value types, так и значения ссылок reference types.

Вопрос, на который дан кривой ответ в статье - это не "в чем особенности реализации", а "Значимые и ссылочные типы — в чём отличия". Основное отличие, вот, по документации:

Variables of reference types store references to their data (objects), while variables of value types directly contain their data.

И то и то переменные, и те и те могут быть локальными (или не-локальными, полями, например), и те и те могут хранится в стеке. Переменные reference types хранят ссылки на данные (объекты), переменные value type хранят непосредственно данные. А про стек - это миф и копипаста.

Погоди, ты сейчас серьёзно string к reference type отнёс?

Всегда им и был. А в чём проблема?

  1. IEnumerable может двигаться только вперед по коллекции, он не может идти назад

  1. IQueryable может двигаться только вперед по коллекции, он не может идти назад

:)

Sign up to leave a comment.

Articles