2013-06-12 5 views
3

У меня возникли проблемы с методом Async в C# /. NET 4. Он добавляет потоки в процесс w3wp, но не освобождает их. Наш сервер в конечном итоге достигает предела потока около 400, а затем пул приложений становится недоступным, пока он перерабатывается.Async Threads не выпущен

Мы используем EndInvoke здесь неправильно?

Вот упрощенный пример, Repros проблему:

[WebMethod] 
    public void Test() 
    { 
     TestFind("test"); 
    } 

    private delegate void TestFindDelegate(String val); 
    private TestFindDelegate tfd; 
    private IAsyncResult iar; 

    public void TestFind(String val) 
    { 
     try 
     { 
      tfd = new TestFindDelegate(this.TestFindAsync); 
      iar = tfd.BeginInvoke(val, null, null); 
     } 
     catch (Exception ex) 
     { 
      String msg = ex.Message; 
     } 
    } 

    //Method runs asynchronously 
    private void TestFindAsync(String val) 
    {    
     try 
     { 
      //Run stuff here 
     } 
     catch (Exception ex) 
     { 
      String msg = ex.Message; 
     } 
     finally 
     { 
      tfd.EndInvoke(iar); //clean up resources 
     } 
    } 

шагов к Repro:
1. Добавьте код, указанный выше в веб-service.asmx
2. Откройте Диспетчер задач, добавить столбец Темы , найдите процесс
3. Откройте Fiddler, перейдите к Composer и введите URL-адрес веб-сервиса/Тест
4. Нажмите «Выполнить» 20-40 раз
5. Следите за количеством потоков при увеличении процесса, но не уменьшите как таковые.

+0

Вы уверены, что это действительно происходит через '// Run stuff here'? –

+0

Да, этот код воспроизводит проблему как есть, даже без ничего в // Запускаем здесь. –

+0

Вы действительно ошибаетесь, EndInvoke() следует вызывать * после *, метод перестает работать. Третий аргумент BeginInvoke() позволяет передать делегат в обратный вызов завершения, вызвать EndInvoke в этом обратном вызове. –

ответ

2

Возможно, проблема не в том, что вы неправильно звоните EndInvoke. При использовании Delegate.BeginInvoke вы всегда должны звонить EndInvoke, и вы должны позвонить ему после Ваш метод завершен. От MSDN:

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

Прямо сейчас, вы отслеживании tfd и iar в переменной, но каждый вызов будет перезаписывать эту переменную. Таким образом, если вы вызываете это 100 раз быстро, вы вызываете только EndInvoke.

Лучшим вариантом было бы просто использовать Task для запуска этого:

public void TestFind(String val) 
{ 
    Task.Factory.StartNew(() => this.TestFindAsync(val)); 
} 

Это будет вызывать это на Threadpool нить, но не требуют EndInvoke вызова, или любой из локальных переменных, которые будут сохранены.

+0

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

+0

@ user2378631 Да - но он не будет вызываться в каждом экземпляре 'tfd' /' iar', если вы вызываете его еще до того, как завершится первая операция async. –

+0

@ user2378631 Это также должно произойти ПОСЛЕ завершения метода. Это означало бы, что в вашей текущей архитектуре вам нужно будет также предоставить обратный вызов и вызвать EndInvoke в этом обратном вызове, а не в самом методе. –

Смежные вопросы