Pull to refresh
18
0
Павел @INC_R

C# developer

Send message

Тогда, согласно вашим определениям, любой код синхронный, потому что он выполняется так, как он написан, а не как ему вздумается. Только это называется не синхронность, а детерменированность.


Вы зачем-то смешиваете логику работы откомпилированного байт-кода (после оптимизации, преобразований и трансформации) и логику исходного кода. Они могут быть разные.

Нет, они одинаковые. Трансформации, компиляция и все прочее не может менять логику.

Ну а в вашем случае, будет возвращено не актуальное значение счётчика.

А я сказал, что мне нужно актуальное, чтобы генерировать идентификаторы? Мне атомарность не нужна, если я хочу, скажем, показать пользователю число просмотров поста и одновременно увеличить его на 1. Если он увидит, что просмотров было 10, а не 17, ничего страшного не случится. А вот увеличение значения счетчика должно работать атомарно.


Пишите на ассемблере — будете точно всё знать.

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

Ничего себе. То есть, например, если надо опросить 1000 сервисов на предмет курса валют или прогноза погоды, то надо делать это строго синхронно, по-очереди, дожидаясь ответа от каждого, а не поставить 1000 асинхронных задач одновременно? Сильное утверждение, поржал.

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

Сколько еще раз надо написать, что при вызове await не происходит остановки, а просто все после await фактически становится callback-ом? Это ровно настолько же синхронизация, насколько синхронизацией является подписка на событие завершения асинхронной операции и выполнение какой-то операции в качестве реакци на это событие. Если это в вашем понимании синхронизация, то пусть так. Но наличие такой "синхронизации" не делает код синхронным. От того, что две асинхронные операции "синхронизированы" так, что одна начинает работать после завершения другой, их выполнение синхронным не станет. Или вы будете с этим спорить?


завершение процесса — не стоит называть асинхронным событием.
Вот если бы ваш таск присылал вам в случайные моменты времени сообщения, и вы на них реагировали — это было бы асинхронным событием.

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

Поэтому ниже вы предлагаете вообще отказаться от async/await. Тогда-то ошибки не будет, просто сломается и починить нормально нельзя будет.


Вы уходите от первоначального вопроса: о том, что это один и тот же код и что он может быть в обоих случаях синхронен или асинхронен (в вашем понимании), но точно не в одном случае так, а в другом иначе.


И "забыл await" — это совершенно другой вопрос. Для этого есть инструментарий, который показывает предупреждения, для этого есть суффикс Async, который намекает на необходимость await, и все такое. Никто не мешает во втором примере вернуть t1, что будет точно такой же ошибкой.

а-синхронный код, в классическом, верном, понимании — это суть, событийно ориентированный код, который построен на колл-беках и/или обработчиках событий

Ну и чем await не угодил? Это ровно то же самое. Визуально (то есть в исходнике) он выглядит как синхронный код с ожиданимями и всем таким, потому что так и задумано. Фактически же await обозначает, что все ниже него будет callback-ом. Довольно грубое описание, но концептуально это ровно так.

Да нет, по моему будет так:

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


В соответствующем окружение вполне себе очевидно, что любая операция может

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

Сюрприз, в первом тоже. При вызове await происходит выход с незавершенной задачей. Это один и тот же код, написанный по разному.

Используйте флаги.

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


А зачем вам дожидаться результата, если он вас не интересует?

Потому что иногда меня интересует, что операция выполнилась, но не интересует непосредственно результат. Например, асинхронная операция меняет что-то в БД и возвращает количество измененных записей. А в данном случае меня это количество не интересует. В другом месте оно нужно, а здесь — нет.


А зачем понимать на высоком уровне детали внутренней реализации метода ReceiveData?

А меня не интересуют детали. Мне нужно понимать, что запрос завершится до выполнения какой-то другой логики.
Например, вот это получает текущее значение счетчика, увеличивает значение счетчика и возвращает то значение, которое было:


public async Task<int> GetCurrentCounterAndIncrement()
{
  var counter = await client.GetCounterAsync();
  client.IncrementCounter();
  return counter;
}

А теперь напишу по-вашему:


public int GetCurrentCounterAndIncrement()
{
  var counter = client.GetCounter();
  client.IncrementCounter();
  return counter;
}

Ничего, что в этом случае неочевидно, что client.GetCounter() асинхронен и теоретически операции могут выполниться в таком порядке, что я сначала обновлю счетчик, а потом получу уже новое значение?

Неужели? То есть, по вашему, первый код синхронный, а второй нет?


// 1
async Task Example1()
{
    await DoSomethingAsync();
    DoSomethingElse();
}

// 2
Task Example2()
{
    var t1 = DoSomethingAsync();
    return t1.ContinueWith(t => DoSomethingElse());
}

И не уходите от вопроса. Что в вашем понимании асинхронный код?


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

Код синхронный, когда исполнение идёт последовательно с ожиданием завершения подзадач.

Видимо, ваш "синхронный код" — это вообще не то, что синхронные/асинхронные операции в общепринятом понимании. Приведите пример "асинхронного кода" в вашем понимании.


И по-вашему, если я начал долгую асинхронную операцию IO, потом долго что-то считал, и потом "жду" результата начатой ранее операции через await — это типа все синхронно?

А какое отношение это имеет:


  1. к вопросу о том, как связаны понятия потоков (thread, нить) и async/await?
  2. к вашему изначальному утверждению, что от использования await исполнение внезапно становится синхронным? Я так и не увидел аргументов к этому утверждению.

Ну очевидно, он блокирует flow, в котором подразумевается, что какие-то операции выполняются после завершения какой-то асинхронной операции. Только с каких пор операция от этого становится синхронной?

Куда выходят? Когда? Куда возвращаются? Причем здесь стек? Причем здесь вообще потоки ("нити"), если async/await не завязан на них и будет спокойно работать в среде, где понятия потоков может вообще не быть?

То, что блокируется не системная "нить", а легковесное "волокно", сути не меняет

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

Приведите пример этого нормального языка, пожалуйста.
И, главное, что мне делать, если нужно проверить, завершилась ли операция, а не ждать результата, например. Или если я хочу дождаться результата и никак не использовать его (альтернатива инструкции await task;).
И каким образом я при взгляде на код вообще должен понимать, на каком этапе в каком состоянии асинхронные операции и есть ли они там вообще.

Кажется, в vs 2017 на эту тему что-то делали, но это актуально не везде. Видимо для этого нужна специальная поддержка в том числе со стороны плагина. Уверен, kekekeks сможет сказать точнее, как это работает и работает ли вообще.

А ничего, что можно сначала вызвать асинхронную функцию, а только потом, после выполнения другой работы, сделать await? Например, так:


var receiveTask = client.ReceiveDataAsync(); // началось получение данных в фоне
CalculateSomething(); // долго считаем что-то, пока параллельно получаются данные
var data = await receiveTask; // досчитали все, что хотели, теперь ждем получения данных

Это все еще синхронно, ведь await все делает синхронным, верно?

Я пробовал недавно голую vs 2017 пол-дня, больше не хочу. Я знаю, что в студии много всякой фигни, она развивается, есть разные рефакторинги и все такое, но субъективно — "спасибо, нет". Хотя бы за медленный поиск текста по всему солюшену или ущербное средство запуска юнит-тестов. А если сравнивать с rider — так в студии даже текстовый редактор откровенно слабый и чекаут сделать невозможно без боли.

Я бы к этому еще добавил то, что из-за маленькой особенности студии в виде 32-битности эта конструкция бытро упирается в лимит по памяти. Голая студия в него вполне влезает, а вот решарпер (или другие крупные расширения) уже умещается плохо. Так что в в том, что виноваты плагины (фактом своего наличия), я не сомневаюсь, и на голой студии проблемы с нехваткой ресурсов, я уверен, не будет. Но только голая студия по сравнению с vs + r# или Rider — примерно как блокнот.

Information

Rating
Does not participate
Location
Санкт-Петербург, Санкт-Петербург и область, Россия
Date of birth
Registered
Activity