WebException(超时)在设置ThreadPool.MaxThreads后从多个线程读取文件时

时间:2013-11-12 23:09:07

标签: c# io threadpool

所以,我追查了这个问题,但我不明白根本原因而且我很好奇。

我有多个线程从本地驱动器读取文件(有时是相同的文件,但通常是不同的文件。这似乎并不重要)。这是测试设置,但在生产中,这些文件是从Web服务器检索的。

无论如何,我注意到,在致电ThreadPool.SetMaxThreads()之后,我收到了读取这些文件的超时。删除该行会使问题消失。我的预感是它与设置异步IO线程的数量(completionPortThreads,第二个参数)有关,但即使我将该值设置为较大的数字(50,100,...),问题仍然存在

删除对SetMaxThreads的调用“修复了”问题,但这意味着我无法增加或减少用于测试目的的线程数。

这是一段重现问题的代码。文件大小无关紧要,因为我的测试文件范围从2KB到3MB不等。

class Program
{
    static void Main(string[] args)
    {           
        _count = 15;
        // Comment this line out and everything works
        ThreadPool.SetMaxThreads(13, 50);
        using (var mre = new ManualResetEvent(false))
        {
            for (int i = 0; i < _count; ++i)
            {
                ThreadPool.QueueUserWorkItem(ThreadFunc, mre);
            }

            mre.WaitOne();
        }
    }

    private static readonly ConcurrentStack<byte[]> _files = new ConcurrentStack<byte[]>();
    private static int _count;

    private static void ThreadFunc(object o)
    {       
        const string path = @"SomeLocalFile";
        var file = ReadFile(path);
        _files.Push(file);
        if (Interlocked.Decrement(ref _count) == 0)
        {
            ((ManualResetEvent)o).Set();
        }
    }

    private static byte[] ReadFile(string uri)
    {
        var request = WebRequest.Create(uri);

        using (var response = request.GetResponse())
        using (var stream = response.GetResponseStream())
        {
            var ret = new byte[stream.Length];
            stream.Read(ret, 0, ret.Length);
            return ret;
        }
    }       
}

所以,是的,不知道这里发生了什么。即使IO线程值很大,我也会在每次测试时超时。我当然错过了一些东西。

1 个答案:

答案 0 :(得分:1)

FileWebRequest是WebRequest.Create()返回的类型,它也使用ThreadPool.QueueUserWorkItem。由于您限制了工作线程,因此FileWebRequest的排队工作永远不会被执行。您需要将max worker threads设置为至少_count + 1(加1,以便至少有一个线程可以处理FileWebRequest排队的工作)。

FileWebRequest.GetRequestStream执行以下操作:

  1. ThreadPool.QueueUserWorkItem(读取文件)
  2. 等到文件被读取或达到超时
  3. 更好的解决方案: 不要将项目排入ThreadPool。改为使用WebRequest.GetResponseAsync。