在ASP.NET中并行执行.NET HttpWebRequests的建议

Kev*_*son 5 c# parallel-processing asp.net-mvc multithreading httpwebrequest

我有一个ASP.NET MVC Web应用程序,它对其他服务器进行REST样式的Web服务调用.我有一个场景,我正在对两个单独的服务进行两次HttpWebRequest调用.我需要他们两个完成继续,但他们的顺序无关紧要.它们可能每个需要1-2秒,我现在按顺序运行它们.并行运行会减少用户响应时间,但最好的方法是什么?

在研究这个时,我可以想到几个选择:

  • 在主线程上执行一个请求,并为另一个请求启动第二个线程.应该创建新线程还是使用线程池?如果我使用游泳池,我该如何调整尺寸?另外,不知道如何将线程重新连接在一起(例如使用ThreadPool.RegisterWaitForSingleObject)?
  • 尝试并利用内置的IAsyncResult支持一个或两个请求.同样,不确定异步请求在哪些线程上执行,因此不确定如何调整线程池的大小.如何将IAsyncResult加入我的主线程?所有的例子我在回调中找到了进程信息,但是我可以在主线程中等待并使用IsCompleted属性?

我需要找到一个既能运作又能大规模运行的解决方案.这就是我担心线程池大小的原因.我不愿意阻止请求,因为他们正在等待可用的线程.

Kev*_*son 1

多线程 WebRequests的答案之一是一种良好且稳定的方法吗?:CSharp使用一个ManualResetEvent event = new ManualResetEvent()引用计数器来Interlocked.Decrement控制event.Set(). 然后主线程通过调用 来等待event.WaitOne()

然而,WaitHandles - Auto/ManualResetEvent 和 Mutex提到,ManualResetEvent“可能比使用各种 Monitor 方法(如 Wait、Pulse 和 PulseAll)慢得多”。

我最终将我的代码基于 Noah Blumenthal 博客文章:异步运行通用任务(流畅)。我确实做了两个更改:实现IDisposable并调用 ,.Close()ManualResetEvent从使用 alock()切换到Interlocked .Increment().Decrement()

public class AsyncQueueManager : IDisposable {
    private readonly ManualResetEvent waitHandle = new ManualResetEvent(true);
    private int count = 0;

    public AsyncQueueManager Queue(Action<object> action) {
        Interlocked.Increment(ref count);
        waitHandle.Reset();
        Action<object> actionWrapper = CreateActionWrapper(action);
        WaitCallback waitCallback = new WaitCallback(actionWrapper);
        ThreadPool.QueueUserWorkItem(waitCallback);
        return this;
    }

    private Action<object> CreateActionWrapper(Action<object> action) {
        Action<object> actionWrapper = (object state) =>
        {
            try {
                action(state);
            } catch (Exception ex) {
                // log
            } finally {
                if (Interlocked.Decrement(ref count) == 0) {
                    waitHandle.Set();
                }
            }
        };
        return actionWrapper;
    }

    public void Wait() {
        waitHandle.WaitOne();
    }
    public void Wait(TimeSpan timeout) {
        waitHandle.WaitOne(timeout);
    }

    public void Dispose() {
        waitHandle.Close();
    }
}
Run Code Online (Sandbox Code Playgroud)