使用ThreadPool线程与长时间运行的ADO.NET查询。这可扩展吗?

时间:2009-11-18 05:59:22

标签: asp.net multithreading ado.net threadpool

我们目前正在增强一个ASP.NET应用程序,可以对许多产品执行引用。

目前,现有的引用引擎基本上是一个大的存储过程(每次调用2-3秒),然后是在过程调用之后运行的少量业务逻辑。

我们正在考虑对每个产品进行多线程调用,以加快一组报价。

我们当前的方法是将每个产品报价工作封装在ThreadPool线程中。这似乎表现得更好,但我有点担心虽然它在少数用户中表现良好,但它能在生产环境中很好地扩展吗?

请注意,目前我们没有使用异步ADO.NET方法。

注意:我们调用ThreadPool的代码具有对请求进行排队的限制,因此我们一次只能使用ThreadPool中可配置数量的线程。我们也不需要在同一页面上等待报价结果,我们允许用户进度并检查更新(报价结果页面使用AJAX检查结果)。

进一步说明:首选解决方案是使用消息队列,因为报价服务是单向操作。但是,该项目的时间表并没有为我们提供时间。

与此同时,我们将修改实现以使用ADO.NET的异步方法(因为这是流程的所有长期运行方面),节省了使用ThreadPool线程的需要。

2 个答案:

答案 0 :(得分:6)

  

使用ThreadPool线程长   运行ADO.NET查询。这是   可伸缩的

简短的回答是否定的,它不可扩展。

原因是ThreadPool线程也用于处理常规ASP.NET请求(BeginInvoke也是如此)。这些线程数量有限,当它们用完时,传入的HTTP请求将被阻塞,直到线程可用。

虽然可以增加ASP.NET线程池中的线程数,但还有其他问题,例如线程数没有修复的事实;它会因负载而缓慢增加。您可以/应该使用异步页面,但仍然存在如何实际运行SP的问题。如果可以的话,切换到该部分的异步ADO.NET方法会好得多。它们的重量也比每个请求使用一个线程轻得多。

如果有帮助,我会在我的书(Ultra-Fast ASP.NET)中详细介绍这个主题。

答案 1 :(得分:1)

ASP.NET有内置的异步处理程序,可以启动请求,它在非处理程序的线程上运行,并且可以在所有内置的第3个线程(与原始请求处理程序线程不同)上完成。我已经习惯了很多次。

http://msdn.microsoft.com/en-us/magazine/cc163725.aspx

给自己写一个小帮手方法来解决这个问题。

/// <summary>
/// On load event override
/// </summary>
/// <param name="e">arguments to the event</param>
protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    string query = this.Page.Request.QueryString["query"];
    if (!string.IsNullOrEmpty(query))
    {
        var pat = new PageAsyncTask(this.BeginAsync, this.EndAsync, this.TimeOut, query);
        this.Page.RegisterAsyncTask(pat);
    }
    string me = string.Format(Format, System.Threading.Thread.CurrentThread.ManagedThreadId, "Onload");
    Trace.Write(me);
}
protected override void Render(HtmlTextWriter writer)
{
    string me = string.Format(Format, System.Threading.Thread.CurrentThread.ManagedThreadId, "Render");
    Trace.Write(me);
    this.Icompleted.Text = DateTime.Now.ToString();
    base.Render(writer);
}
/// <summary>
/// start the async task
/// </summary>
/// <param name="sender">original caller</param>
/// <param name="e">unused arguments</param>
/// <param name="cb">call back routine</param>
/// <param name="state">saved stated</param>
/// <returns>IAsyncResult to signal ender</returns>
private IAsyncResult BeginAsync(object sender, EventArgs e, AsyncCallback cb, object state)
{
    this.bsc = new YourWebServiceReferenceGoesHere();
    return this.bsc.BeginGetResponseXml("1", (string)state, "10", "1", cb, state);
}

/// <summary>
/// when the task completes
/// </summary>
/// <param name="ar">the async result</param>
private void EndAsync(IAsyncResult ar)
{
    XmlResponse response = this.bsc.EndGetResponseXml(ar);
    this.bsc.Close();
    this.bsc = null;
    this.PostProcess(response);
}

private void TimeOut(IAsyncResult ar)
{
    // currently we do nothing here.
}

/// <summary>
/// 
/// </summary>
private void PostProcess(XmlResponse response )
{
    string me = string.Format(Format, System.Threading.Thread.CurrentThread.ManagedThreadId, "bingsearch");
    Trace.Write(me);
    var xds = new XmlDataSource 
    { 
        EnableCaching = false, 
        CacheDuration = 0, 
        Data = response.Xml, 
        Transform = this.RemoveNamespaces() 
    };
    this.some.DataSource = xds;
    this.some.DataBind();
}