Несколько вопросов про потоки, lock, async в asp.net
От: gamburger  
Дата: 08.11.16 21:46
Оценка: :)
У меня просто несколько вопросов на разные темы по C#.
0)
http://stackoverflow.com/questions/1048884/c-overriding-return-types
Т.к. стэковерфлоу — странный сайт, он, вроде, нужен для того, чтобы что-то узнавать, но ничего там нельзя спросить, пока не наберешь репутацию 80 левела, то спрошу тут:
Там спросили про то, как поменять тип virtual проперти в наследованном классе. Т.е., чтобы dog.Excrement и cat.Excrement возвращали разные типы объектов.
Ответ, который там стоит accepted, предлагает 2 решения, первое — через generic (тут все просто и понятно), и второе ("EDIT: A new solution, using extension methods and a marker interface...") — через создание интерфейса, экстеншена и 40 метров дополнительного (по сравнению с 1м вариантом) кода.
Второй вариант предлагает получение значения нужного проперти через dog.Excrement и cat.StronglyTypedExcrement(). Моя проблема тут состоит в том, что я просто не могу понять, что хотели сказать этим кодом, ведь он вообще не отвечает на поставленный вопрос "как поменять тип virtual проперти в наследованном классе". Тут у класса Cat вызывается НЕ ПРОПЕРТИ Excrement , а ЛЕВЫЙ МЕТОД StronglyTypedExcrement. Я бы мог предложить аналогичную реализацию через левое проперти, но намного короче — просто в класс Cat добавить дополнительно проперти StronglyTypedExcrement, которое бы возвращало объект типа RadioActivePoo , и не нужно никаких интерфейсов, экстеншенов и всего остального.
В общем, я так и не смог осознать, что там хотели показать с помощью 2-го варианта. Может, кто-нибудь тут раскроет мне глаза.
1)
https://ru.wikipedia.org/wiki/Double_checked_locking

Семантика некоторых языков программирования[каких?] такова, что потоку А разрешено присвоить разделяемой переменной ссылку на объект, который находится в процессе инициализации (что в общем-то вполне однозначно нарушает причинно-следственную связь, ведь программист вполне явно просил присваивать переменной ссылку на объект [то есть — опубликовать ссылку в общий доступ] — в момент после инициализации, а не в момент до инициализации).

Из-за этого они ввели ключевое слово volatile...
Кто-нибудь может объяснить, зачем нужно позволять присваивать ссылку на еще не инициализированный объект (они даже сами там и написали, что это нарушает причинно-следственную связь)?
Зачем вместо того, чтобы вводить какие-то костыли volatile, просто не сделать так, чтобы ссылка была создана, только когда сам объект тоже инициализирован?
И получается же, что не важно, 2 потока или 1, т.к., даже если 1 поток, то он тоже может использовать не до конца инициализированную переменную7 Я имею ввиду ситуацию
var x = new ObjectX();
 if(x != null){x.toDo();}

, тут возможно, что ссылка создана, т.е. код войдет в if , но упадет на вызове toDo(), т.к. объект не до конца создан? Хотя я на практике такого никогда не видел. Или в случае одного потока гарантированно следующая инструкция после new вызовется уже после полного создания объекта?
2)
Почему нельзя заюзать lock без скобок с объектом (т.е. вместо lock(lockObj) заюзать просто lock) ? В этом случае компилятор автоматом мог бы сгенерить в этом классе неявное проперти static readonly object _lockObj, которое бы подставилось вместо пропущенного lockObj. Это ж, вроде, очевидная идея для синтаксического сахара, или нет?
И почему компилер не выдает ошибку на lock(new Object()) , когда внутри lock каждый раз создаешь новый объект? Смысл этого = 0 , т.к. никаких блокировок в этом случае не будет, ну и разумно, чтобы компилер выдавал ошибку (или хотя бы предупреждения).
3)
Про Asp.Net и потоки.
Я не мог понять, для чего может быть нужно возвращать таски из экшенов в новом MVC, начал читать эту статью
http://www.codeguru.com/csharp/.net/net_asp/mvc/creating-asynchronous-actions-in-asp.net-mvc.htm

However, the allotted thread invokes the GetCustomerData() asynchronously and is immediately returned to the thread pool to serve other requests. When the remote service call returns the required data, another thread from the thread pool is allotted to finish the remainder of the Index() method code. Thus instead of blocking a thread for the entire duration of the processing, a thread is released as soon as the network operation starts and the processing resumes on some other thread when the network operation returns. Even in this case the total time taken for the processing is 10 seconds but the thread is freed to serve other requests.

Дочитать до этой строчки. Не понятно, как оно может так работать с тасками.
Допустим, если я по старинке (без тасков) запускаю синхронный GetCustomerData(), который получает данные с сервиса, создавая для этого поток
new Thread(()=>{data = helper.GetCustomerData();}).Start()
то поток, вызванный asp.net, после запуска моего потока сразу вернется в пул, а новый поток последовательно:
1)сделает запрос до сервиса
2)будет ждать, пока сервис ответит (прослушивать сеть)
3)получив ответ (результат), присвоит его в переменную data.
Все эти 3 события, как я понимаю, будут последовательно происходить в одном потоке (который я создаю в new Thread).
В статье (в цитате выше) написано, что преимущество вызова сервиса асинхронно в том, что после возврата из потока asp.net и до того, как сервис вернет результат, все потоки освобождаются и ни один из них не задействован, поэтому может быть использован в других целях.
Т.е., судя по описанию, если запускать асинхронный GetCustomerData() асинхронно с помощью Task, так:
List<Customer> data = await helper.GetCustomerDataAsync();
то поток, вызванный asp.net, после await тоже сразу вернется в пул (*immediately returned to the thread pool*), но что я не могу понять, это:

When the remote service call returns the required data, another thread from the thread pool is allotted to finish the remainder of the Index() method code.

Из вышенаписанного следует, что поток, вызванный asp.net, после await уже вернулся в пул, но при этом Task НЕ запустил новый поток сразу, поток выделится только ПОСЛЕ того, как придет ответ от сервиса.
А что тогда будет прослушивать сеть? Должен же быть объект, который прослушивает сеть (как в п.2 в списке выше), и который при получении отклика выполнит коллбэк (как в п.3 в списке выше). Но поток asp.net уже освобожден и вернулся в пул потоков и больше не выполняет наш код, а новый поток Task еще не выделен. Тогда где и как может существовать этот объект?
Я бы мог это понять, если бы алгоритм выглядел так: во время await поток asp.net, перед тем, как освободиться, через Task запускает другой поток, который последовательно:
1)сделает запрос до сервиса
2)будет ждать, пока сервис ответит (прослушивать сеть)
3)получив ответ (результат), присвоит его в переменную data.
Этот алгоритм похож на тот, который у меня в первом примере через new Thread, поэтому он мне понятен, но в статье-то алгоритм описан по-другому.
Но в случае с моим алгоритмом, мне не понятно, зачем было бы нужно запускать GetCustomerData() в отдельном потоке, т.к., освобождая 1 поток, мы сразу же выделяем другой, и не понятно, чем это было бы лучше простого синхронного вызова GetCustomerData() внутри экшена.
Если рассматривать алгоритм из статьи, то можно было бы предположить, что объект, который будет слушать отклик и вызывать коллбэк, будет существовать в каком-то третьем потоке, который Task специально для этого скрыто выделил, но в статье почему-то решили про это не упоминать. Но тогда снова не понятно, в чем преимущество асинхронного запроса через Task, если все-равно в каждый момент времени будет выделен какой-то поток, но тут их аж целых 3 (кстати, не понятно, зачем для выполнения коллбэка тут выделять 3-й поток, если его можно было бы выполнять во втором).
Отредактировано 08.11.2016 21:55 gamburger . Предыдущая версия . Еще …
Отредактировано 08.11.2016 21:51 gamburger . Предыдущая версия .
c# .net
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.