-1

In my C# I have the following code:

System.Threading.ThreadPool.QueueUserWorkItem(delegate
{
     GetWeatherTimetable(new string[] {"London","UK"});
}, null);

Explanation: I create a new ThreadPool.QueueUserWorkItem to call onto Multi Threading and run the function GetWeatherTimetable. This function does lots of heavy async work that awaits for user input and things of that nature that cannot be changed and sends ~20 async HTTP Requests using RestSharp. An example of such would be:

RestClient client = new RestClient();
var getreq = new RestRequest(url, Method.GET);

getreq.AddHeader("accept", "*/*");

var getreqcomplete = await client.ExecuteAsync(getreq);

When running 1 Threaded Task, the whole process finishes anywhere from 5-7 seconds depending on response times from requests. Even with 2 or 3 the difference in time is still marginal and cannot be seen, but when I run a mere 10 tasks, the process takes anywhere from 20-30 seconds and I start seeing inconsistencies in the function and it updates much slower and does everything much slower. I started debugging what was happening and I saw that almost everytime I would call on an await or send a request, the Thread.CurrentThread.ManagedThreadId would change, sometimes clashing with other threads.

For example: One Thread moves from ThreadID1 to ThreadID2 and another Thread from ThreadID4 changes to ThreadID2 on the same line of code, therefore clashing the onto the same Thread.

Is it possible that this is what is causing the inconsistencies in completion time? I read here about await having to start a new Thread when it is called and using that instead. Is there anyway that I can disable that and stay on one Thread at a time?

  • `Is it possible that this is what is causing the inconsistencies in completion time?` Your tasks don't "clash". Tasks are enqueued on the threadpool, and the threads pick up work when they're free. A given thread does not execute multiple tasks at the same time. For your performance problem, it would be easier to use a profiler to understand what's going on. In any case, are you sure that it's not just the HTTP service that you are querying that has a throttling mechanism, triggered when you send multiple requests at once? – Kevin Gosse Aug 29 '20 at 21:56
  • `This function does lots of heavy async work that awaits for user input and things` sounds like something that would require synchronization, and therefore could degrade when using multiple threads if not done correctly – Kevin Gosse Aug 29 '20 at 21:58

1 Answers1

0

Someone with more experience than me with this is welcome to correct me if my understanding is incomplete/incorrect.

The way I understand it, there is a thread pool. Let's assume in this example that it contains 2 threads: T1 and T2.

Now, the await/async pattern works with Tasks and what actually happens is that it creates state machines that allow stopping and resuming so that when a task is idle (e.g.: waiting for http request), it can just wait while some other Task runs.

The tasks are in a Task pool. E.g.: S1, S2, S3, S4, S5, S6 for a total of 6 Tasks concurrently.

When those tasks are actually being run, they need to be run on a Thread (T1 or T2). So, you could end up with S1 starting some work on T1 and then awaiting. Meanwhile, S2 could do some work on T1 as well (or could do it on T2 - the framework has heuristics for to determine how to do the balancing). The specifics of how this can happen are not known to me.

Here's a thread with a similar topic: Is Task.Factory.StartNew() guaranteed to use another thread than the calling thread?

So, to answer the question, yes, you could have two tasks running on the same thread. So you need to make sure your code will be safe (e.g.: no deadlocks possible).

Now as for performances... It really depends. If your Tasks are doing work that mostly awaits.. there's no need for multiple threads as a a single thread can do the actual work for all of them.

e.g.: Work: "-", Sleep: "_"

S1: ---__________________________---'
S2: ___---__________________________
S3: __________---___________________
S4: _______--____--_________________
S5: ________________-------_________
S6: _______________________-________

Since the tasks are mostly waiting and need to perform work at different times, they could use the same thread efficiently. Throwing more threads at it will only result in more unnecessary memory clutter and context switching, thus worsening the overall performance. Additionally, for every piece of synchronization required between threads (or tasks), the new threads loose more and more value.

DereckM
  • 274
  • 2
  • 11
  • I don't want the 2 tasks to run on the same thread, I want them to not clash threads as I think this is what is making them slow down more than they should. About the thread locking, I searched it up and found [this](https://stackoverflow.com/questions/2411410/thread-deadlock-example-in-c-sharp). So if I lock it to an object, would the thread then not change on awaits? – johnstart2312 Aug 29 '20 at 20:38