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();
    }
Matthew Ransley
  • 106
  • 1
  • 10
  • 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. – Alexander Petrov Sep 01 '20 at 17:27
  • Creating and running a `Thread` is time-consuming. Use the `ThreadPool` instead. – Alexander Petrov Sep 01 '20 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 https://learn.microsoft.com/en-us/dotnet/api/system.net.servicepointmanager.defaultconnectionlimit?view=netcore-3.1 – JPK Sep 01 '20 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. – Franz Gleichmann Sep 01 '20 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. – Enigmativity Sep 03 '20 at 23:01

1 Answers1

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);
    }
}
Roar S.
  • 8,103
  • 1
  • 15
  • 37