Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking if a thread returned to thread pool

How can I check if a thread returned to the thread pool, using VS C# 2015 debugger?

What's problematic in my case is the fact that it cannot be detected by debugging line by line.

async Task foo()
{
    int y = 0;
    await Task.Delay(5);
    // (1) thread 2000 returns to thread pool here...
    while (y<5) y++;
}

async Task testAsync()
{
    Task task = foo();
    // (2) ... and here thread 2000 is back from the thread pool, to run the code below. I want
    // to confirm that it was in the thread pool in the meantime, using debugger.
    int i = 0;
    while (i < 100)
    {
        Console.WriteLine("Async 1 before: " + i++);

    }
    await task;
}

In the first line of testAsync running on thread 2000, foo is called. Once it encounters await Task.Delay(5), thread 2000 returns to thread pool (allegedly, I'm trying to confirm this), and the method waits for Task.Delay(5) to complete. In the meantime, the control returns to the caller and the first loop of testAsync is executed on thread 2000 as well.

So between two consecutive lines of code, the thread returned to thread pool and came back from there. How can I confirm this with debugger? Possibly with Threads debugger window?

To clarify a bit more what I'm asking: foo is running on thread 2000. There are two possible scenarios:

  1. When it hits await Task.Delay(5), thread 2000 returns to the thread pool for a very short time, and the control returns to the caller, at line (2), which will execute on thread 2000 taken from the thread pool. If this is true, you can't detect it easily, because Thread 2000 was in the thread pool during time between two consecutive lines of code.

  2. When it hits await Task.Delay(5), thread 2000 doesn't return to thread pool, but immediately executes code in testAsync starting from line (2)

I'd like to verify which one is really happening.

like image 844
user4205580 Avatar asked Nov 15 '15 09:11

user4205580


People also ask

How do you monitor thread pool?

You can monitor the usage of Default Executor thread pool by using ThreadPoolMXBean . Key Performance data that is available for ThreadPool : Threads in the pool that represents the pool size. Active threads that are serving requests.

When using thread pool What happens to a given thread?

Once a thread in the thread pool completes its task, it's returned to a queue of waiting threads. From this moment it can be reused. This reuse enables applications to avoid the cost of creating a new thread for each task.

What happens if you spawn too many threads?

You need to prevent too many threads from being spawned, a problem that could potentially result in a denial of service owing to exhausted system resources.

What is the difference between thread and thread pool?

A thread pool is - as the name suggests - a pool of worker threads which are always running. Those threads then normally take tasks from a list, execute them, then try to take the next task. If there's no task, the thread will wait.


2 Answers

There is a major mistake in your assumption:

When it hits await Task.Delay(5), thread 2000 returns to the thread pool

Since you don't await foo() yet, when thread 2000 hits Task.Delay(5) it just creates a new Task and returns to testAsync() (to int i = 0;). It moves on to the while block, and only then you await task. At this point, if task is not completed yet, and assuming the rest of the code is awaited, thread 2000 will return to the thread pool. Otherwise, if task is already completed, it will synchronously continue from foo() (at while (y<5) y++;).

EDIT:

what if the main method called testAsync?

When synchronous method calls and waits async method, it must block the thread if the async method returns uncompleted Task:

void Main()
{
    var task = foo();
    task.Wait(); //Will block the thread if foo() is not completed.
}

Note that in the above case the thread is not returning to the thread pool - it is completely suspended by the OS.

Maybe you can give an example of how to call testAsync so that thread 2000 returns to the thread pool?

Assuming thread 2k is the main thread, it cannot return to the thread pool. But you can use Task.Run(()=> foo()) to run foo() on the thread pool, and since the calling thread is the main thread, another thread pool thread will pick up that Task. So the following code:

static void Main(string[] args)
{
    Console.WriteLine("main started on thread {0}", Thread.CurrentThread.ManagedThreadId);
    var testAsyncTask = Task.Run(() => testAsync());
    testAsyncTask.Wait();
}
static async Task testAsync()
{
    Console.WriteLine("testAsync started on thread {0}", Thread.CurrentThread.ManagedThreadId);
    await Task.Delay(1000);
    Console.WriteLine("testAsync continued on thread {0}", Thread.CurrentThread.ManagedThreadId);
}

Produced (on my PC) the following output:

main started on thread 1
testAsync started on thread 3
testAsync continued on thread 4
Press any key to continue . . .

Threads 3 and 4 came from and returned to the thread pool.

like image 187
shay__ Avatar answered Sep 22 '22 01:09

shay__


You can print out the Thread.CurrentThread.ManagedThreadId to the console. Note that the thread-pool is free to re-use that same thread to run continuations on it, so there's no guarantee that it'll be different:

void Main()
{
    TestAsync().Wait();
}

public async Task FooAsync()
{
    int y = 0;
    await Task.Delay(5);
    Console.WriteLine($"After awaiting in FooAsync:
                        {Thread.CurrentThread.ManagedThreadId }");

    while (y < 5) y++;
}

public async Task TestAsync()
{
    Console.WriteLine($"Before awaiting in TestAsync:
        {Thread.CurrentThread.ManagedThreadId }");
    Task task = foo();
    int i = 0;
    while (i < 100)
    {
        var x = i++;
    }

    await task;
    Console.WriteLine($"After awaiting in TestAsync:
                        {Thread.CurrentThread.ManagedThreadId }");
}

Another thing you can check is ThreadPool.GetAvailableThreads to determine if another worker has been handed out for use:

async Task FooAsync()
{
    int y = 0;
    await Task.Delay(5);

    Console.WriteLine("Thread-Pool threads after first await:");
    int avaliableWorkers;
    int avaliableIo;
    ThreadPool.GetAvailableThreads(out avaliableWorkers, out avaliableIo);
    Console.WriteLine($"Available Workers: { avaliableWorkers}, 
                        Available IO: { avaliableIo }");

    while (y < 1000000000) y++;
}

async Task TestAsync()
{
    int avaliableWorkers;
    int avaliableIo;
    ThreadPool.GetAvailableThreads(out avaliableWorkers, out avaliableIo);

    Console.WriteLine("Thread-Pool threads before first await:");
    Console.WriteLine($"Available Workers: { avaliableWorkers}, 
                        Available IO: { avaliableIo }");
    Console.WriteLine("-------------------------------------------------------------");

    Task task = FooAsync();

    int i = 0;
    while (i < 100)
    {
        var x = i++;
    }

    await task;
}

On my machine, this yields:

Thread-Pool threads before first await:
Available Workers: 1023, Available IO: 1000
----------------------------------------------
Thread-Pool threads after first await:
Available Workers: 1022, Available IO: 1000
like image 26
Yuval Itzchakov Avatar answered Sep 20 '22 01:09

Yuval Itzchakov