子线程完成后主线程不会继续

时间:2012-09-06 19:40:13

标签: c# multithreading manualresetevent waitone countdownevent

我正在尝试在我的应用程序中使用多线程。方法test5尝试从Internet获取一些内容,而main线程在继续其他工作之前等待所有线程完成。

但是我的main线程在调用test5后没有返回,我的控制台行Done Inside!!thread all got back!!永远不会到达。

如何解决此问题?

class Program
{
    static void Main(string[] args)
    {
        string[] url =
        {
            "http://...", "http://...", "http://...", "http://...", "http://..."
        };

        test5(url); 
        Console.WriteLine("thread all got back!!");

        // Do some other work after all threads come back
        Console.ReadLine();
    }

    private static void test5(string[] _url)
    {
        int numThreads = _url.Length;
        ManualResetEvent resetEvent = new ManualResetEvent(false);
        int toProcess = numThreads;

        for (int i = 0; i < numThreads - 1; i++)
        {
            new Thread( delegate() {
                testWebWorking(_url[i]);
                if (Interlocked.Decrement(ref toProcess) == 0)
                    resetEvent.Set();
            }).Start();
        }
        resetEvent.WaitOne();
        Console.WriteLine("Done inside!!");
    }

    private static void test6(string[] _url)
    {
        int numThreads = _url.Length;
        var countdownEvent = new CountdownEvent(numThreads);

        for (int i = 0; i < numThreads - 1; i++)
        {
            new Thread(delegate() {
                testWebWorking(_url[i]);
                countdownEvent.Signal();
            }).Start();
        }
        countdownEvent.Wait();
        Console.WriteLine("Done inside!!");
    }

    private static void testWebWorking(object url)
    {
        Console.WriteLine("start {0}", Thread.CurrentThread.ManagedThreadId);
        string uri = (string)url;
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
        request.KeepAlive = true;
        request.Timeout = 5000;
        request.ReadWriteTimeout = 5000;
        request.Proxy = null;

        try
        {
            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            {
                //Console.WriteLine(response.ContentType + "; uri = " + uri);
                Stream receiveStream = response.GetResponseStream();
                Encoding encode = System.Text.Encoding.GetEncoding("utf-8");
                // Pipes the stream to a higher level stream reader with the required encoding format. 
                StreamReader readStream = new StreamReader(receiveStream, encode);
                //Console.WriteLine("\r\nResponse stream received.");
                Char[] read = new Char[256];
                // Reads 256 characters at a time.    
                int count = readStream.Read(read, 0, 256);
                //Console.WriteLine("HTML...\r\n");
                String str = "";
                while (count > 0)
                {
                    // Dumps the 256 characters on a string and displays the string to the console.
                    str = new String(read, 0, count);
                    //Console.Write(str);
                    count = readStream.Read(read, 0, 256);
                }
                //Console.WriteLine(str);
                // Releases the resources of the response.
                response.Close();
                // Releases the resources of the Stream.
                readStream.Close();

                Console.WriteLine("end {0}", Thread.CurrentThread.ManagedThreadId);
            }
        }
        catch (WebException ex)
        {
            //Console.WriteLine(ex.GetBaseException().ToString() );
            //Console.WriteLine(url);
            Console.WriteLine("time out !!");
        }
        finally
        {
            request.Abort();
            request = null;
            GC.Collect();
        }
    }
}

1 个答案:

答案 0 :(得分:2)

看看这个:

for (int i = 0; i < numThreads - 1; i++)

你只是开始numThreads - 1个帖子。您的计数器从numThreads开始并倒计时,因此它只会达到1,而不是0。

顺便说一句,这也被打破了::

for (int i = 0; i < numThreads - 1; i++)
{
    new Thread( delegate()
    {
        testWebWorking(_url[i]);
        ...
    }
    ...
}

在这里,您正在捕获委托中的变量 i,因此它在执行时将具有i所具有的任何值。您可以多次测试同一个URL,并跳过其他URL。相反,您应该将i的值复制到循环中的变量中,以便每次捕获不同的变量:

// Fixed the loop boundary as well
for (int i = 0; i < numThreads; i++)
{
    int copy = i;
    new Thread(() => // Use a lambda expression for brevity
    {
        // Fix naming convention for method name, too...
        TestWebWorking(_url[copy]);
        if (Interlocked.Decrement(ref toProcess) == 0))
        {
            resetEvent.Set();
        }
    }).Start()
}

你的两种方法都有同样的问题。

我个人不会在这里使用for循环 - 我会使用foreach循环:

private static void TestWithResetEvent(string[] urls)
{
    ManualResetEvent resetEvent = new ManualResetEvent(false);
    int counter = urls.Length;

    foreach (string url in urls)
    {
        string copy = url;
        Thread t = new Thread(() =>
        {
            TestWebWorking(copy);
            if (Interlocked.Decrement(ref toProcess) == 0))
            {
                resetEvent.Set();
            }
        });
        t.Start();
    }
    resetEvent.WaitOne();
    Console.WriteLine("Done inside!!");
}

或者,使用Parallel.ForEach会更简单,{{1}}就是为这类事物设计的。