3

我在使用 C#/.NET 4 中的 Async 方法时遇到问题。它将线程添加到 w3wp 进程,但不会释放它们。我们的服务器最终达到了 400 左右的线程限制,然后应用程序池在回收时变得无法访问。

我们在这里使用 EndInvoke 是否错误?

这是一个重现问题的简化示例:

    [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. 将上述代码添加到 web service.asmx
2. 打开任务管理器,添加列 Threads,找到进程
3. 打开 Fiddler,进入 Composer,输入 web service url/Test
4. 点击 Execute 20-40次
5. 观察进程的线程数增加,但没有减少。

4

1 回答 1

2

问题很可能是您没有EndInvoke正确调用。使用时Delegate.BeginInvoke,必须始终调用EndInvoke,并且必须方法完成后调用。来自MSDN

无论您使用哪种技术,始终调用 EndInvoke 来完成您的异步调用。

tfd现在,您正在跟踪iar一个变量,但每次调用都会覆盖该变量。因此,如果你快速调用它 100 次,你只会调用EndInvoke一次。

更好的选择是只使用Task来运行它:

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

这将在线程池线程上调用它,但不需要EndInvoke调用或保存任何局部变量。

于 2013-06-12T23:05:49.913 回答