Асинхронні операції в ASP.NET MVC використовують потік із ThreadPool на .NET 4


158

Після цього питання мені стає зручно при використанні операцій з асинхронізацією в ASP.NET MVC. Отже, я написав два повідомлення про це:

У мене занадто багато непорозумінь щодо асинхронних операцій на ASP.NET MVC.

Я завжди чую це речення: додаток може краще масштабуватися, якщо операції виконуються асинхронно

І я чула дуже багато таких пропозицій: якщо у вас величезний обсяг трафіку, вам може бути краще не виконувати запити асинхронно - споживання 2 додаткових потоків для обслуговування одного запиту відбирає ресурси від інших вхідних запитів.

Я думаю, що ці два речення непослідовні.

У мене не так багато інформації про те, як працює нитка пулів на ASP.NET, але я знаю, що потоковий пул має обмежений розмір для потоків. Отже, друге речення має бути пов'язане з цим питанням.

І мені хотілося б знати, чи використовує асинхронні операції в ASP.NET MVC нитку з ThreadPool на .NET 4?

Наприклад, коли ми реалізовуємо AsyncController, як структурується програма? Якщо я отримую величезний трафік, чи корисно реалізувати AsyncController?

Чи є хтось там, хто може зняти цю чорну завісу перед моїми очима і пояснити мені угоду про асинхронію на ASP.NET MVC 3 (NET 4)?

Редагувати:

Я читав цей документ майже сотні разів і розумію основну угоду, але все-таки маю плутанину, оскільки там надто багато непослідовних коментарів.

Використання асинхронного контролера в ASP.NET MVC

Редагувати:

Припустимо, у мене є дії контролера, як показано нижче (але це не реалізація AsyncController):

public ViewResult Index() { 

    Task.Factory.StartNew(() => { 
        //Do an advanced looging here which takes a while
    });

    return View();
}

Як ви бачите тут, я запускаю операцію і забуваю про неї. Потім я повертаюся негайно, не чекаючи, коли це буде завершено.

У цьому випадку, чи має це використовувати нитку з нитки? Якщо так, після його завершення, що відбувається з цією ниткою? Чи GCзаходить та чиститься відразу після його завершення?

Редагувати:

Для відповіді @ Darin, ось зразок коду асинхронізації, який спілкується з базою даних:

public class FooController : AsyncController {

    //EF 4.2 DbContext instance
    MyContext _context = new MyContext();

    public void IndexAsync() { 

        AsyncManager.OutstandingOperations.Increment(3);

        Task<IEnumerable<Foo>>.Factory.StartNew(() => { 

           return 
                _context.Foos;
        }).ContinueWith(t => {

            AsyncManager.Parameters["foos"] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });

        Task<IEnumerable<Bars>>.Factory.StartNew(() => { 

           return 
                _context.Bars;
        }).ContinueWith(t => {

            AsyncManager.Parameters["bars"] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });

        Task<IEnumerable<FooBar>>.Factory.StartNew(() => { 

           return 
                _context.FooBars;
        }).ContinueWith(t => {

            AsyncManager.Parameters["foobars"] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });
    }

    public ViewResult IndexCompleted(
        IEnumerable<Foo> foos, 
        IEnumerable<Bar> bars,
        IEnumerable<FooBar> foobars) {

        //Do the regular stuff and return

    }
}

Не впевнений у відповіді, але варто відзначити асинхронність та багатопоточність - це різні речі. Таким чином, можна було б мати фіксовану кількість потоків з асинхронним керуванням. Що станеться, коли одна сторінка повинна блокуватися для скажімо, введення / виводу, інша сторінка отримає шанс запуститись у тому ж потоці. Ось як обидва ці твердження можуть бути правдивими, асинхронізація може зробити все швидше, але занадто багато потоків є проблемою.
Кріс Чілверс

@ChrisChilvers Так, багатоаспектність не завжди потрібна при асинхронній роботі. Я вже зрозумів це, але, думаю, у мене немає контролера, наскільки я можу сказати. AsyncController розкручує скільки ниток хоче з моєї точки зору, але не впевнений у цьому. Чи існує поняття про потоковий пул і для таких настільних програм, як WPF? Я думаю, що кількість потоків не є проблемою для таких програм.
тугберк

6
Подивіться відео Threading with Jeff Richter
oleksii

Я думаю, що проблема (і, таким чином, непослідовність) у тому, що друге твердження використовує асинхронність, коли воно означає багато потоків. Це може бути тому, що саме так asp.net реалізував сторінки асинхронізації, і, таким чином, конкретна реалізація плутала проблему (оскільки назва функції, яка спричиняє проблему, була б сторінками асинхронізації), але я не впевнений у конкретній реалізації . Отже, або вони означають "багато потоків", або вони означають "асинхронізувати сторінки в asp.net версії X", оскільки майбутні версії можуть змінити реалізацію. Або вони просто мають на увазі використання пулу потоків для асинхронізації на сторінці.
Кріс Чілверс

@ChrisChilvers о, чоловіче! Я більше
плутаюсь

Відповіді:


177

Ось чудова стаття, яку я рекомендую вам прочитати, щоб краще зрозуміти асинхронну обробку в ASP.NET (що в основному представляють асинхронні контролери).

Розглянемо спочатку стандартну синхронну дію:

public ActionResult Index()
{
    // some processing
    return View();
}

Коли робиться запит на цю дію, нитка витягується з пулу потоків і тіло цієї дії виконується на цій нитці. Отже, якщо обробка всередині цієї дії відбувається повільно, ви блокуєте цей потік для всієї обробки, тому цей потік не можна повторно використовувати для обробки інших запитів. В кінці виконання запиту потік повертається до пулу потоків.

Тепер візьмемо приклад асинхронного шаблону:

public void IndexAsync()
{
    // perform some processing
}

public ActionResult IndexCompleted(object result)
{
    return View();
}

Коли запит надсилається до дії Index, нитка виводиться з пулу потоків і IndexAsyncвиконується тіло методу. Після того, як тіло цього методу закінчить виконання, нитка повертається до пулу ниток. Потім, використовуючи стандарт AsyncManager.OutstandingOperations, як тільки ви сигналізуєте про завершення операції з асинхронізацією, інша нитка витягується з пулу потоків і IndexCompletedна ній виконується тіло дії, а результат надається клієнту.

Тож, що ми можемо побачити на цій схемі, це те, що один клієнтський HTTP-запит може бути виконаний двома різними потоками.

Тепер цікава частина відбувається всередині IndexAsyncметоду. Якщо у вас є операція блокування всередині нього, ви повністю витрачаєте всю ціль асинхронних контролерів, оскільки ви блокуєте робочу нитку (пам’ятайте, що тіло цієї дії виконується на потоці, витягнутому з пулу потоків).

Тож коли ми можемо реально скористатися асинхронними контролерами, про які ви можете запитати?

ІМХО ми можемо отримати найбільше, коли ми маємо інтенсивні операції вводу / виводу (такі як дзвінки до бази даних та мережеві дзвінки до віддалених служб). Якщо ви працюєте з процесором, асинхронні дії не принесуть вам великої користі.

То чому ми можемо отримати вигоду від інтенсивних операцій вводу / виводу? Тому що ми могли використовувати порти завершення вводу / виводу . IOCP надзвичайно потужні, оскільки ви не споживаєте жодних потоків або ресурсів на сервері під час виконання всієї операції.

Як вони працюють?

Припустимо, ми хочемо завантажити вміст віддаленої веб-сторінки за допомогою методу WebClient.DownloadStringAsync . Ви викликаєте цей метод, який зареєструє IOCP в операційній системі та повернеться негайно. Під час обробки всього запиту на вашому сервері не використовуються жодні потоки. Все відбувається на віддаленому сервері. Це може зайняти багато часу, але вам все одно, оскільки ви не ставите під загрозу робочі потоки. Після отримання відповіді сигналізується IOCP, з пулу потоків виводиться потік і виконується зворотний виклик на цьому потоці. Але, як ви бачите, протягом усього процесу ми не монополізували жодної нитки.

Те саме стосується таких методів, як FileStream.BeginRead, SqlCommand.BeginExecute, ...

А як щодо паралелізації декількох дзвінків до бази даних? Припустимо, у вас була дія синхронного контролера, в якій ви виконували 4 блокування викликів бази даних послідовно. Неважко підрахувати, що якщо кожен виклик бази даних займає 200 мс, для виконання вашого контролера буде потрібно приблизно 800 мс.

Якщо вам не потрібно буде виконувати ці дзвінки послідовно, чи паралельне їх підвищення покращить продуктивність?

Це велике питання, на яке непросто відповісти. Може так, може, ні. Це повністю залежатиме від того, як ви реалізуєте ці дзвінки до бази даних. Якщо ви використовуєте контролери асинхронізації та порти завершення вводу / виводу, як обговорювалося раніше, ви збільшите продуктивність цієї дії контролера та інших дій, оскільки ви не будете монополізувати робочі потоки.

З іншого боку, якщо ви їх погано реалізуєте (з блокуванням виклику бази даних, що виконується на потоці з пулу потоків), ви в основному зменшите загальний час виконання цієї дії приблизно до 200 мс, але ви витратили б 4 робочих нитки, щоб ви можливо, погіршило виконання інших запитів, які можуть стати голодними через відсутніх потоків у пулі для їх обробки.

Тож це дуже важко, і якщо ви не відчуваєте готовності виконати обширні тести на своїй програмі, не застосовуйте асинхронні контролери, оскільки ймовірність того, що ви нанесете більше шкоди, ніж користі. Реалізуйте їх, лише якщо у вас є причина: наприклад, ви визначили, що стандартні дії синхронного контролера є вузьким місцем для вашої програми (після виконання обширних тестів навантаження та, звичайно, вимірювань).

Тепер розглянемо ваш приклад:

public ViewResult Index() { 

    Task.Factory.StartNew(() => { 
        //Do an advanced looging here which takes a while
    });

    return View();
}

Коли надходить запит на дію "Індекс", нитка виводиться з пулу потоків для виконання свого тіла, але його тіло лише планує нове завдання за допомогою TPL . Отже виконання дії закінчується, і потік повертається до пулу потоків. Крім того, TPL використовує нитки з пулу потоків для їх обробки. Тож навіть якщо початковий потік було повернуто до пулу потоків, ви намалювали інший потік із цього пулу, щоб виконати тіло завдання. Отже, ви поставили під загрозу 2 нитки зі свого дорогоцінного пулу.

Тепер розглянемо наступне:

public ViewResult Index() { 

    new Thread(() => { 
        //Do an advanced looging here which takes a while
    }).Start();

    return View();
}

У цьому випадку ми вручну нерестуємо нитку. У цьому випадку виконання тіла вказівної дії може зайняти трохи більше часу (оскільки нерестування нової нитки коштує дорожче, ніж витягнення з існуючого пулу). Але виконання розширеної операції ведення журналу буде здійснено на потоці, який не є частиною пулу. Тож ми не піддаємо небезпеці нитки з пулу, які залишаються безкоштовними для подачі чергових запитів.


1
Дійсно детально, дякую! Припустимо, що у нас є 4 асинхронні завдання ( System.Threading.Task), що виконуються всередині IndexAsyncметоду. Всередині цих операцій ми здійснюємо db-дзвінки до сервера. Отже, всі вони є інтенсивними операціями вводу / виводу, правда? У такому випадку ми створюємо 4 окремих потоку (або отримуємо 4 окремих потоку з пулу потоків)? Якщо припустити, що у мене є багатоядерна машина, яку вони також будуть працювати паралельно, правда?
тугберк

10
@tugberk, виклики бази даних - це операції вводу / виводу, але все залежатиме від того, як ви їх реалізуєте. Якщо ви використовуєте блокуючий дзвінок до бази даних, такий як SqlCommand.ExecuteReaderви витрачаєте все, оскільки це виклик блокування. Ви блокуєте нитку, на якій виконується цей виклик, і якщо ця нитка є потоком з пулу, це дуже погано. Ви будете користуватися тільки при використанні портів введення / виводу завершення: SqlCommand.BeginExecuteReader. Якщо ви не використовуєте IOCP незалежно від того, чим займаєтесь, не використовуйте контролери async, оскільки ви нанесете більше шкоди, ніж користі для загальної продуктивності вашої програми.
Дарин Димитров

1
Ну, більшість часу я спочатку використовую EF-код. Я не впевнений, чи підходить вона. Я склав зразок, який показує, що я взагалі роблю. Я оновив питання, чи можете ви подивитися?
тугберк

3
@tugberk, ви запускаєте їх паралельно, тому загальний час виконання менше порівняно з тим, якщо ви запускаєте їх послідовно. Але для їх запуску ви використовуєте робочі потоки. Ну насправді EF лінивий, тому коли ви цього _context.Fooне робите, ви нічого не виконуєте. Ви просто будуєте дерево виразів. Будьте надзвичайно обережні з цим. Виконання запиту відкладається лише тоді, коли ви починаєте перерахувати через набір результатів. І якщо це трапиться на думку, це може бути катастрофічно для виступу. Щоб охоче виконати додавання запиту EF .ToList()в кінці.
Дарин Димитров

2
@tugberk, вам знадобиться інструмент для перевірки навантаження, щоб імітувати декількох користувачів паралельно на вашому веб-сайті, щоб побачити, як він поводиться під великим навантаженням. Mini Profiler не може імітувати завантаження вашого веб-сайту. Це може допомогти вам побачити та оптимізувати ваші запити ADO.NET та профайлювати єдиний запит, який марний, коли вам потрібно побачити, як ваш сайт веде себе в реальній ситуації, коли багато користувачів на нього нападають.
Дарин Димитров

49

Так - всі нитки походять із пулу потоків. Ваш додаток MVC вже є багатопоточним, коли запит надходить у новій нитці, буде взято з пулу та використаний для обслуговування запиту. Цей потік буде "заблокований" (від інших запитів) до повного обслуговування та завершення запиту. Якщо в пулі немає потоку, запиту доведеться почекати, поки він не з’явиться.

Якщо у вас є контролери асинхронізації, вони все одно отримують нитку з пулу, але, обслуговуючи запит, вони можуть відмовитись від потоку, очікуючи, що щось станеться (і цей потік може бути переданий іншому запиту) і коли для початкового запиту потрібна нитка знову вона отримує одну з басейну.

Різниця полягає в тому, що якщо у вас є багато тривалих запитів (де нитка чекає відповіді від чогось), у вас може не вистачати потоків з пулу, щоб обслуговувати навіть основні запити. Якщо у вас є контролери асинхронізації, у вас більше немає потоків, але ті потоки, які чекають, повертаються в пул і можуть обслуговувати інші запити.

Приклад майже з реального життя ... Подумайте про це, як сісти в автобус, п’ять людей чекають, щоб сісти, перший ввімкнеться, оплатить і сідає (водій обслуговує їх запит), ви входите (водій обслуговує ваш запит), але ви не можете знайти свої гроші; коли ти кинеш у кишені, водій відмовляється від тебе і отримує наступних двох людей (обслуговуючи їхні запити), коли ти знайдеш свої гроші, водій знову починає з вами працювати (заповнюючи ваш запит) - п'ятий чоловік повинен чекати, поки Ви закінчили, але третій та четвертий люди були обслужені, поки ви проходили на півдорозі. Це означає, що водій є єдиною ниткою з басейну, а пасажири - це запити. Було надто складно писати, як це буде працювати, якби було два водії, але ви можете собі уявити ...

Без контролера асинхроніки пасажирам, які стоять позаду вас, доведеться чекати віків, поки ви шукаєте свої гроші, тим часом водій автобуса не буде працювати.

Отже, висновок полягає в тому, що якщо багато людей не знають, де їх гроші (тобто потрібен тривалий час, щоб відповісти на те, що просив драйвер), контролери асинхронізу можуть цілком допомогти в пропускній спробі, прискоривши процес з деяких. Без контролера aysnc кожен чекає, поки людина повністю попереду. Але не забувайте, що в MVC у вас багато драйверів автобусів на одній шині, тому асинхронізація не є автоматичним вибором.


8
Дуже приємна аналогія. Дякую.
Пітсбург DBA

Опис мені сподобався. Дякую
Омер Кансізоглу

Відмінний спосіб пояснити це. Дякую,
Ананд Вяс

Ваша відповідь у поєднанні з відповіддю Даріна підсумовує весь механізм, що стоїть за асинхронними контролерами, що це таке, і що ще важливіше, що це не так!
Нірман

Це приємна аналогія, але моє єдине запитання таке: хлопець, котрий за вашою аналогією мацає в кишені, був би якоюсь роботою / обробкою в нашому додатку ... тож які процеси, які працюють, коли ми випускаємо нитку? Похмуро це ще одна нитка? То що, ми тут отримуємо?
Томуке

10

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

public ViewResult Index() { 

    Task.Factory.StartNew(() => { 
        //Do an advanced looging here which takes a while
    });

    return View();
}

належить до другої категорії. Користувач отримає швидший відгук, але загальна завантаженість на сервері вище, тому що він повинен виконувати ту саму роботу + обробляти нитку.

Іншим прикладом цього може бути:

public ViewResult Index() { 

    Task.Factory.StartNew(() => { 
        //Make async web request to twitter with WebClient.DownloadString()
    });

    Task.Factory.StartNew(() => { 
        //Make async web request to facebook with WebClient.DownloadString()
    });


    //wait for both to be ready and merge the results

    return View();
}

Оскільки запити працюють паралельно, користувачеві не доведеться чекати так довго, як якщо б це було зроблено послідовно. Але ви повинні усвідомити, що ми тут використовуємо більше ресурсів, ніж якщо б ми працювали послідовно, тому що ми запускаємо код на багатьох потоках, тоді як у нас також чекають потоки.

Це зовсім чудово за клієнтським сценарієм. І там досить часто вписувати синхронний довгий запуск коду в нове завдання (запустити його на інший потік), також підтримуйте інтерфейс адаптивним або паралельним, щоб зробити його швидшим. Хоча нитка все ще використовується протягом усієї тривалості. На сервері з високим навантаженням це може призвести до негайної реакції, оскільки ви фактично використовуєте більше ресурсів. Про це вас попереджали люди

Контролери Async у MVC мають ще одну мету. Сенс у тому, щоб уникнути того, щоб навколо них не було нічого робити (що може зашкодити масштабуванню). Це дійсно важливо лише в тому випадку, якщо API, який ви викликаєте, має методи асинхронізації. Як і WebClient.DowloadStringAsync ().

Справа в тому, що ви можете дозволити повернути потік для обробки нових запитів до тих пір, поки веб-запит не буде закінчений, коли він зателефонує вам, зворотній виклик, який отримає той самий або новий потік, і завершить запит.

Сподіваюся, ви зрозуміли різницю між асинхронним і паралельним. Подумайте про паралельний код як про код, де ваша нитка сидить навколо і чекайте результату. Хоча асинхронний код - це код, про який вам буде повідомлено, коли код буде зроблено, і ви зможете знову працювати над ним, тим часом потік може виконувати іншу роботу.


6

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

Асинхронні операції гарантують, що ви ніколи не блокуєте дію, оскільки виконується існуюча. ASP.NET має асинхронну модель, яка дозволяє виконувати кілька запитів одночасно. Можна було б встановити чергу до запитів і обробити їх FIFO, але це не буде добре розширюватись, коли у вас є сотні запитів у черзі і кожен запит займає 100 мс для обробки.

Якщо у вас величезний обсяг трафіку, можливо , вам буде краще не виконувати запити асинхронно, оскільки для обслуговування запитів можуть не бути додаткові ресурси . Якщо немає запасних ресурсів, ваші запити змушені стояти в черзі, тривати експоненціально довше або прямо відмовитися, і в цьому випадку асинхронні накладні витрати (мутекси та операції з переключенням контексту) нічого не дають вам.

Що стосується ASP.NET, ви не маєте вибору - для цього використовується асинхронна модель, адже саме це має сенс для моделі сервер-клієнт. Якщо ви повинні писати свій власний код, який використовує шаблон асинхронізації, щоб спробувати покращити масштаб, якщо ви не намагаєтесь керувати ресурсом, який поділяється між усіма запитами, ви не побачите жодних удосконалень, оскільки вони вже завернуті в асинхронному процесі, який не блокує нічого іншого.

Зрештою, це все суб'єктивно, поки ви фактично не подивитеся на те, що викликає вузьке місце у вашій системі. Іноді очевидно, де допоможе асинхронний візерунок (запобігаючи блокуванню ресурсів у черзі). Зрештою, лише вимірювання та аналіз системи може вказувати, де можна отримати ефективність.

Редагувати:

У вашому прикладі Task.Factory.StartNewвиклик буде чергувати операцію в пулі потоків .NET. Характер ниток басейну з нитками має бути використаний повторно (щоб уникнути витрат на створення / знищення багатьох ниток). Після завершення операції потік відпускається назад в пул, щоб повторно використовувати його іншим запитом (збирач сміття насправді не втягується, якщо ви не створили деякі об'єкти в своїх операціях, і в такому випадку вони збираються як зазвичай обстеження).

Що стосується ASP.NET, то тут ніяких спеціальних операцій немає. Запит ASP.NET завершується без поваги до асинхронного завдання. Єдине занепокоєння може викликати, якщо пул потоків насичений (тобто немає ниток, доступних для обслуговування запиту зараз, а налаштування пулу не дозволяють створювати більше потоків), у цьому випадку запит блокується в очікуванні запуску завдання, поки нитка пулу не стане доступною.


Дякую! Після того, як я прочитав вашу відповідь, я відредагував це питання на зразок коду. Ви можете подивитися?
туфберк

У вас є для мене чарівне речення: Task.Factory.StartNewвиклик буде чергувати операцію в пулі потоків .NET. . У цьому контексті, який тут правильний: 1-) Він створює нову нитку, і коли це буде зроблено, цей потік повертається до нитки потоків і чекає, щоб його знову використали. 2-) Він отримує нитку з нитки і ця нитка повертається до нитки і чекає, щоб її знову використали. 3-) Це найефективніший підхід і може зробити будь-який з них.
тугберк

1
Пул потоків створює нитки, як вони потрібні, і переробляє нитки, коли вони не використовуються. Це точно поведінка відрізняється у різних версіях CLR. Конкретну інформацію про неї можна знайти тут msdn.microsoft.com/en-us/library/0ka9477y.aspx
Пол Тернер

Він починає формуватися в моїй свідомості зараз. Отже, CLR володіє пулом потоків, правда? Наприклад, додаток WPF також має поняття пулу потоків, і він також має справу з пулом там.
тугберк

1
Басейн ниток - це власна річ в межах CLR. Інші компоненти, які «знають» про пул, вказують на те, що вони використовують нитки пулу Thread Pool (де це доречно), а не створюють і знищують власні. Створення або знищення потоку є досить дорогою операцією, тому використання пулу - це великий приріст ефективності для короткочасних операцій.
Пол Тернер

2

Так, вони використовують нитку з пулу ниток. Насправді є досить чудовий посібник від MSDN, який вирішить усі ваші запитання та багато іншого. Я вважав це досить корисним у минулому. Перевір!

http://msdn.microsoft.com/en-us/library/ee728598.aspx

Тим часом коментарі + пропозиції, які ви чуєте про асинхронний код, слід сприймати із зерном солі. Для початку, просто зробити щось асинхронне не обов'язково покращує його масштаб, а в деяких випадках може погіршити масштаб вашої програми. Інший коментар, який ви опублікували про "величезний обсяг трафіку ...", також правильний лише в певних контекстах. Це дійсно залежить від ваших операцій та того, як вони взаємодіють з іншими частинами системи.

Коротше кажучи, багато людей мають багато думок щодо асинхронізації, але вони можуть бути невірними поза контекстом. Я б сказав, що зосередиться на ваших точних проблемах, і пройдіть базове тестування продуктивності, щоб побачити, що контролери асинхронізації тощо реально поводяться з вашим додатком.


Я читав цей документ, можливо, сотні разів, і все одно в мене стільки плутанини (можливо, проблема в мені, хто знає). Коли ви озираєтесь навколо, на ASP.NET MVC ви бачите стільки суперечливих коментарів про асинхронність, скільки ви можете бачити на моє запитання.
буксир

для останнього речення: всередині дії контролера я запитував базу даних 5 разів окремо (мені довелося), і все займало приблизно 400 мс. Потім я реалізував AsyncController і запустив їх паралельно. Час відповіді різко скоротилося до прибл. 200 мс. Але я поняття не маю, скільки ниток він створює, що відбувається з цими потоками після того, як я з ними закінчуюсь, чи GCприходить і очищає їх відразу після того, як я закінчу, щоб у моєму додатку не було витоку пам'яті тощо. Будь-яка ідея з цього боку.
тугберк

приєднайте налагоджувач і дізнайтеся.
AR

0

По-перше, це не MVC, а IIS, який підтримує пул потоків. Отже, будь-який запит, що надходить до програми MVC або ASP.NET, подається з потоків, які підтримуються в пулі потоків. Лише роблячи додаток Asynch, він викликає цю дію в іншій нитці і негайно звільняє потік, щоб можна було прийняти інші запити.

Я пояснив те ж саме з детальним відео ( http://www.youtube.com/watch?v=wvg13n5V0V0/ "Контролери MVC Asynch та голодування ниток"), яке показує, як відбувається голодування ниток у MVC та як його мінімізувати за допомогою MVC Контролери Asynch. Я також виміряв черги запитів, використовуючи perfmon, щоб можна було побачити, як зменшуються черги запитів для асинхронізації MVC та як найгірше для операцій Synch.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.