1

On a (stoneage) Windows Mobile 5.0 device I have a problem with the following demo code:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;

namespace TaskTest1
{
class Program
{

    static List<String> Urls = new List<String>()
        {
            "https://www.google.de",
            "https://www.wikipedia.de",
            "https://kjdsfhkjfkhls.de", //non-existent
            "https://kjdsfhkfgddfgfjfkhls.de", //non-existent
            "https://www.msdn.com", //repeated call to same address
            "https://www.msdn.com" };

    static string TimeStamp()

    {
        return DateTime.Now.ToString("hh:mm:ss.fff");
    }

    static void WriteLine(String S)
    {
        Console.WriteLine(String.Format("{0} {1}", TimeStamp(),S));
        System.Diagnostics.Debug.WriteLine(String.Format("{0} {1}", TimeStamp(), S));
    }

    static void Test1Url(string Url)
    {
        WriteLine(String.Format("Testing: {0}",Url));
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
        request.Timeout = 2000;
        try
        { 
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            if (response.StatusCode == HttpStatusCode.OK)
            {
                using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                {
                    long BytesRead = reader.ReadToEnd().Length;
                    WriteLine(String.Format("{0} responded with {1} bytes", Url, BytesRead));
                }

            }
            else
            {
                WriteLine(String.Format("{0} returned error {1}", Url, response.StatusCode));
            }
        }
        catch (System.Net.WebException e)
        {
            WriteLine(String.Format("Exception: call to {0} failed with error {1}", Url, e.Status));
        }

    }

    static void Main(string[] args)
    {
        List<Thread> Threads = new List<Thread>();
        // create one thread per url
        foreach(String url in Urls)
            {
                Thread T = new Thread(() => Test1Url(url));
                Threads.Add(T);
                T.Start();
            }
        // Wait for all threads to complete
        foreach (Thread T in Threads)
        {
            T.Join();
        }
        // ended by debugger
        while (true) {};

    }

}
}

What it should do is concurrently test some https connections, wait 2s for a response and log the outcome to the console.

On Windows 10, VS 2019, .net 4.x (but also if compiled against .net 2.0), the code gives the desired results:

11:54:00.222 Testing: https://www.google.de
11:54:00.232 Testing: https://www.wikipedia.de
11:54:00.237 Testing: https://kjdsfhkfgddfgfjfkhls.de
11:54:00.227 Testing: https://kjdsfhkjfkhls.de
11:54:00.273 Testing: https://www.msdn.com
11:54:00.224 Testing: https://www.msdn.com
11:54:00.557 Exception: call to https://kjdsfhkjfkhls.de failed with error NameResolutionFailure
11:54:00.565 Exception: call to https://kjdsfhkfgddfgfjfkhls.de failed with error NameResolutionFailure
11:54:00.687 https://www.wikipedia.de responded with 4205 bytes
11:54:00.776 https://www.google.de responded with 46759 bytes
11:54:00.940 Exception: call to https://www.msdn.com failed with error SendFailure
11:54:00.949 Exception: call to https://www.msdn.com failed with error SendFailure

On Windows Mobile, VS 2008, .net 2.0 I get

11:43:59.000 Testing: https://www.msdn.com
11:43:59.000 Testing: https://www.msdn.com
11:43:59.000 Testing: https://www.msdn.com
11:43:59.000 Testing: https://www.msdn.com
11:43:59.000 Testing: https://www.msdn.com
11:43:59.000 Testing: https://www.msdn.com
11:44:01.000 Exception: call to https://www.msdn.com failed with error Timeout
11:44:01.000 Exception: call to https://www.msdn.com failed with error Timeout
11:44:01.000 Exception: call to https://www.msdn.com failed with error Timeout
11:44:02.000 Exception: call to https://www.msdn.com failed with error Timeout
11:44:02.000 Exception: call to https://www.msdn.com failed with error Timeout
11:44:02.000 Exception: call to https://www.msdn.com failed with error Timeout

Sometimes, in very rare cases, I also have seen:

11:43:59.000 Testing: https://www.google.de
11:43:59.000 Testing: https://www.msdn.com
11:43:59.000 Testing: https://www.msdn.com
11:43:59.000 Testing: https://www.msdn.com
11:43:59.000 Testing: https://www.msdn.com
11:43:59.000 Testing: https://www.msdn.com

Seems that my Url string is not maintained correctly. There is a race condition, and the task tends to pick up the last string in the list.

What am I doing wrong?

Nimral
  • 657
  • 8
  • 25
  • 1
    Does this answer your question? [Captured Closure (Loop Variable) in C# 5.0](https://stackoverflow.com/questions/16264289/captured-closure-loop-variable-in-c-sharp-5-0) – canton7 Jul 10 '20 at 10:14
  • I can see the problem, just not sure how to put it into words. I think @canton7 is correct. – Neil Jul 10 '20 at 10:15
  • Actually, [this one's a bit better](https://stackoverflow.com/questions/4684320/starting-tasks-in-foreach-loop-uses-value-of-last-item?). I'll close this as a dup with links to some other questions which have good answers, that explain what's going on here. You're not the first person to fall foul of this, which is why the language design team decided to make one of the language's only breaking changes in C# 5 to fix it! – canton7 Jul 10 '20 at 10:20
  • Bullseye :-) Thanks! Strange how the "rare case" has happened. Maybe me halicunating after hours of trying to fix this myself :-) – Nimral Jul 10 '20 at 10:53
  • It's because the thread actually managed to get started before the next loop iteration occurred. The issue occurs because the loop changes the value of `url` before the new thread has a chance to read the previous value of `url` – canton7 Jul 10 '20 at 11:00

0 Answers0