异步线程未释放

3
我在C#/.NET 4中遇到了异步方法的问题。它会向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
        }
    }

重现步骤:
1. 将上述代码添加到 web service.asmx 中
2. 打开任务管理器,添加 Threads 列,找到进程
3. 打开 Fiddler ,进入 Composer,输入 web service url/Test
4. 点击执行 20-40 次
5. 观察进程的线程计数增加,但不会减少。


你确定代码实际上执行到了 //Run stuff here 这里吗? - Reed Copsey
是的,这段代码可以重现问题,即使在//Run stuff here中什么也没有。 - John McMillion
你确实做错了,EndInvoke()必须在方法停止运行之后调用。BeginInvoke()的第三个参数允许你传递一个委托给完成回调,在该回调中调用EndInvoke。 - Hans Passant
如果您想发布解决方案,请将其作为“答案”发布,而不是作为对问题的编辑。问题应该只包含问题本身。 - Servy
1个回答

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

无论使用哪种技术,都要调用EndInvoke以完成异步调用。

现在,您正在使用一个变量跟踪tfdiar,但每次调用都会覆盖该变量。因此,如果您快速调用此函数100次,则只会调用一次EndInvoke
更好的选择是只使用Task来运行此代码:
public void TestFind(String val)
{
    Task.Factory.StartNew(() => this.TestFindAsync(val));
}

这将在线程池线程上调用此方法,但不需要调用EndInvoke,也不需要保存任何本地变量。

当我通过它进行步进时,EndInvoke 在 finally 块中被触发。每次调用都会覆盖全局变量吗? - John McMillion
@user2378631 是的 - 但如果在第一个异步操作完成之前再次调用,它不会在每个tfd/iar实例上被调用。 - Reed Copsey
@user2378631 它还需要在方法完成后发生。这意味着,在您当前的架构中,您还需要提供一个回调,并在该回调中调用EndInvoke,而不是在方法本身中调用。 - Reed Copsey
好的,非常感谢您提供的见解和备选代码建议。为了学习和为其他可能访问这个问题的人提供资源,我们该如何重新设计上面的代码以使其正确工作?我们只需传入变量,而不是在方法外全局设置吗? - John McMillion
@user2378631 请查看 http://msdn.microsoft.com/en-us/library/2e08f6yc.aspx 中关于使用回调方法的部分 - 这就是你需要做的。 - Reed Copsey
那似乎可以工作,谢谢!我添加了可行的解决方案,请告诉我是否存在任何可能的问题。再次感谢您的帮助! - John McMillion

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接