0

I wrote some simple code to grab some key values from a json string, but when i go to multi thread it, to make it quickly grab the value multiple times, it gets slower each time. Im not quite sure why this is happening, i was thinking it might have had something to do with my cpu? Adding a wait function seems to fix it, but makes it go extremely slow, im out of ideas and im not sure as to what is causing it.

Output:

Single Threaded
598
Elapsed=0.558
598
Elapsed=0.121
598
Elapsed=0.127
598
Elapsed=0.127
598
Elapsed=1.167
598
Elapsed=0.122
598
Elapsed=0.124
598
Elapsed=0.145
598
Elapsed=0.146
598
Elapsed=0.12
------------------------------
Multi Threading
598
Elapsed=0.129
598
Elapsed=0.179
598
Elapsed=0.227
598
Elapsed=0.299
598
Elapsed=0.326
598
Elapsed=0.396
598
Elapsed=0.427
598
Elapsed=0.505
598
Elapsed=0.525
598
Elapsed=0.703

private static void Test2() {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        using (WebClient wb = new WebClient()) {
            wb.Proxy = null;
            string data = wb.DownloadString("https://google.com");
            sw.Stop();
            Console.WriteLine("Elapsed={0}", (double)sw.ElapsedMilliseconds / 1000);
        }
    }
    static void Main(string[] args) {

        Console.WriteLine("Single Threaded");

        Test2();
        Test2();
        Test2();
        Test2();
        Test2();
        Test2();
        Test2();
        Test2();
        Test2();
        Test2();

        Console.WriteLine("------------------------------");
        Console.WriteLine("Multi Threading");

        for (int i = 0; i < 10; i++) {
            void Multi() {
                Test2();
            }
            new Thread(Multi).Start();
        }
        Console.Read();
    }
5
  • Each subsequent thread is waiting for access to the network. Therefore, the execution time increases. But the total execution time of multithreaded code will be less. Commented Sep 1, 2020 at 17:27
  • Creating and running a Thread is time-consuming. Use the ThreadPool instead. Commented Sep 1, 2020 at 17:39
  • 2
    You're likely exceeding the default number of concurrent network connections, which is 2 for a console app. You can adjust the the ServicePointManager.DefaultConnectionLimit property to 10 and see how that affects it. The other option is async, which does magic with the IO at a hardware level, which you've discovered. See learn.microsoft.com/en-us/dotnet/api/… Commented Sep 1, 2020 at 18:33
  • have you looked at the total execution time of all workloads together? that's where parallel processing shines, and that's where the overhead of parallelising workloads pays off. Commented Sep 1, 2020 at 19:18
  • Do keep in mind that every time you call new Thread(...) you're allocating a minimum of 1MB of memory for its stack. That's time consuming. For any multithreading code to be useful it must be doing a lot of CPU work to make up for the start up time. Commented Sep 3, 2020 at 23:01

1 Answer 1

4

Just for comparing; I made a little rewrite using async code. Lot of reuse from OPs code, I know there is some old stuff, but let's keep it like that for now.

Single Threaded: Elapsed=2.688

Multi Threaded: Elapsed=0.317

AMD 8-Core CPU

public class ThreadingTests
{
    private static async Task Test2()
    {
        await Task.Delay(1); // only here to make method async

        using var wb = new WebClient { Proxy = null };
        var data = wb.DownloadString("https://www.roblox.com/users/inventory/list-json?assetTypeId=41&cursor=&itemsPerPage=100&pageNumber=1&userId=539720021");
        dynamic parsed = JObject.Parse(data);
    }

    [Test]
    public async Task SingleTest()
    {
        Console.WriteLine("Single Threaded");
        var sw = new Stopwatch();
        sw.Start();

        for (var i = 0; i < 10; i++)
        {
            await Test2();
        }
        
        sw.Stop();
        Console.WriteLine("Elapsed={0}", (double)sw.ElapsedMilliseconds / 1000);
    }

    [Test]
    public async Task MultiTest()
    {
        Console.WriteLine("Multi Threaded");
        var sw = new Stopwatch();
        sw.Start();

        var taskList = new List<Task>();
        for (var i = 0; i < 10; i++)
        {
            taskList.Add(Test2());
        }

        await Task.WhenAll(taskList);

        sw.Stop();
        Console.WriteLine("Elapsed={0}", (double)sw.ElapsedMilliseconds / 1000);
    }
}
Sign up to request clarification or add additional context in comments.

6 Comments

This is how you should run the code in parallel. (y)
It's a good solution, but it doesn't answer the question: "Why?"
@AlexanderPetrov: I agree, working on it, just finishing another case. BR Roar
It is better to use DownloadStringTaskAsync method. Even better, use HttpClient instead of the deprecated WebClient. Just by the way.
@AlexanderPetrov: Just wanted to stay as close to OPs original code as possible. I don't know his environment.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.