-1

I have a for-loop that creates a new Thread each iteration. In short, my loop is creating 20 threads that does some action, at the same time.

My goal inside each of these threads, is to create a DateTime variable with a start time, execute an operation, and create a DateTime variable with an end time. Hereafter I'll take the difference between these two variables to find out, how long this operation took in this SPECIFIC thread. Then log it out.

However that isn't working as expected, and I'm confused on why. It seems like it justs "adds" the time to the variables, each iteration of a new thread, instead of creating a completely new and fresh version of the variable, only to be taking into consideration in that specific thread.

This is my for-loop code:

for(int i = 0; i < 20; i++)
{
    Thread thread = new Thread(() =>
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        RESTRequest(Method.POST, ....), 
        sw.Stop();

        Console.WriteLine("Result took (" + sw.Elapsed.Seconds + " seconds, " + sw.Elapsed.Milliseconds + " milliseconds)");
    });
    thread.IsBackground = true;
    thread.Start();
}

Long operation function:

public static string RESTRequest(Method method, string endpoint, string resource, string body, SimplytureRESTRequestHeader[] requestHeaders = null, SimplytureRESTResponseHeader[] responseHeaders = null, SimplytureRESTAuthentication authentication = null, SimplytureRESTParameter[] parameters = null)
{
    var client = new RestClient(endpoint);

    if(authentication != null)
    {
        client.Authenticator = new HttpBasicAuthenticator(authentication.username, authentication.password);
    }

    var request = new RestRequest(resource, method);

    if (requestHeaders != null)
    {
        foreach (var header in requestHeaders)
        {
            request.AddHeader(header.headerType, header.headerValue);
        }
    }

    if(body != null)
    {
        request.AddParameter("text/json", body, ParameterType.RequestBody);
    }

    if(parameters != null)
    {
        foreach (var parameter in parameters)
        {
            request.AddParameter(parameter.key, parameter.value);
        }
    }

    IRestResponse response = client.Execute(request);

    if (responseHeaders != null)
    {
        foreach (var header in responseHeaders)
        {
            var par = new Parameter();
            par.Name = header.headerType;
            par.Value = header.headerValue;

            response.Headers.Add(par);
        }
    }

    var content = response.Content;

    return content;
}

This is my results:

enter image description here

EDIT: I also tried using the Stopwatch class, but it didn't do any difference, but definitely more handy. I also Added the long operation for debugging.

Kevin Jensen Petersen
  • 423
  • 2
  • 22
  • 43
  • 6
    You really don't want to use DateTime objects to measure code execution time - use a Stopwatch instead. – J. Steen May 15 '17 at 11:27
  • We really need to know what's going on in the operation in the middle there. – rory.ap May 15 '17 at 11:30
  • @J.Steen -- i'm curious about that. Can you elaborate? – rory.ap May 15 '17 at 11:31
  • The Stopwatch didn't do any difference, same results. In the middle I'm simply calling a REST API through RestSharp (Http) by requesting the endpoint, waiting, and receiving a response. – Kevin Jensen Petersen May 15 '17 at 11:31
  • @rory.ap I'll refer you to one of my favourite questions on the subject: http://stackoverflow.com/questions/28637/is-datetime-now-the-best-way-to-measure-a-functions-performance - In short, a Stopwatch has low overheard and high resolution. – J. Steen May 15 '17 at 11:32
  • With a simulated work by Thread.Sleep(200) the result for each thread is nearly 200ms as expected. Your problem is not in that code. Seems you have some locking inside your thread which force them to run serialized. – Sir Rufo May 15 '17 at 11:33
  • @J.Steen -- thanks, that's interesting. Makes sense. – rory.ap May 15 '17 at 11:35
  • Something in the `Operation that takes about 200 milliseconds` is totally wrong because execution time is growing... why not post that code? – EpicKip May 15 '17 at 11:35
  • 1
    Or you accessing some shared resource on API side which your requesting. That can be a reason for blocking all responses – Fabio May 15 '17 at 11:36
  • I added the operation code in the question. – Kevin Jensen Petersen May 15 '17 at 11:41
  • @KevinJensenPetersen probably remote side can't process your reqest faster? Did you try to do simle get to www.*.com? – gabba May 15 '17 at 11:52
  • If you replace `RESTRequest(Method.POST, ....),` with `Thread.Sleep(1000);` what happens? If it works correctly, then the inescapable conclusion is that your `RESTRequest()` has something which blocks other threads. – Matthew Watson May 15 '17 at 11:57

1 Answers1

3

There is a limitation for concurrent calls to the same ServicePoint.

The default is 2 concurrent connections for each unique ServicePoint.

Add System.Net.ServicePointManager.DefaultConnectionLimit = 20; to raise that limit to match the thread count.

System.Net.ServicePointManager.DefaultConnectionLimit

You can also set this value in config file

<system.net>
  <connectionManagement>
    <add address="*" maxconnection="20" />
  </connectionManagement>
</system.net>
Sir Rufo
  • 18,395
  • 2
  • 39
  • 73
  • Can you change this in the App.config? – Kevin Jensen Petersen May 15 '17 at 11:45
  • Yes, you can ;o) – Sir Rufo May 15 '17 at 11:49
  • I just tried this, it still didn't seem to do a difference. It still just looks like it's being synchronous even tho it's inside a thread. Like the picture above. – Kevin Jensen Petersen May 15 '17 at 11:49
  • But you can see, that two threads are running concurrently - there must be a limitation on your machine or on the remote machine – Sir Rufo May 15 '17 at 11:55
  • I see. Yeah It might be a limitation of my remote machine. I've created a REST API through WebAPI in C#. Do I also need to enable this setting on my WebAPI? – Kevin Jensen Petersen May 15 '17 at 11:56
  • Did you read the docs from the link I gave you? *When an app is running as an **ASP.NET** host, it is not possible to alter the value of this property through the config file if the autoConfig property is set to true. However, you can change the value programmatically when the autoConfig property is true. Set your preferred value once, when the AppDomain loads.* – Sir Rufo May 15 '17 at 11:59
  • My main app (from above) is a Console App, so here I can set the setting you specified. But my question is, does this setting ALSO need to be set on my WebAPI (The endpoint I'm CALLING from the Console) – Kevin Jensen Petersen May 15 '17 at 12:02