Основной поток не продолжается после завершения дочерних потоков

Я пытаюсь использовать многопоточность в моем приложении. Метод test5 пытается извлечь некоторый контент из Интернета, в то время как 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 ответ

Решение

Посмотри на это:

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, который предназначен именно для такого рода вещей.

Другие вопросы по тегам