简体   繁体   English

检查线程是否返回到线程池

[英]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? 如何使用VS C#2015调试器检查线程是否返回到线程池?

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. 在线程2000上运行的testAsync的第一行中,调用foo 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. 一旦遇到await Task.Delay(5) ,线程2000返回到线程池(据称,我正在尝试确认这一点),并且该方法等待Task.Delay(5)完成。 In the meantime, the control returns to the caller and the first loop of testAsync is executed on thread 2000 as well. 同时,控件返回给调用者,并且第一个testAsync循环testAsync线程2000上执行。

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? 可能使用Threads调试器窗口?

To clarify a bit more what I'm asking: foo is running on thread 2000. There are two possible scenarios: 为了澄清我的要求: foo在线程2000上运行。有两种可能的情况:

  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. 当它命中await Task.Delay(5) ,线程2000返回线程池很短的时间,并且控制返回到调用者,在第(2)行,它将在从线程池获取的线程2000上执行。 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 . 如果这是真的,则无法轻松检测到它,因为Thread 2000 在两个连续代码行之间的时间段内位于线程池中。

  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) 当它命中await Task.Delay(5) ,线程2000不会返回到线程池,但会立即从第(2)行开始执行testAsync代码

I'd like to verify which one is really happening. 我想验证哪一个真的发生了。

You can print out the Thread.CurrentThread.ManagedThreadId to the console. 您可以将Thread.CurrentThread.ManagedThreadId打印到控制台。 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: 请注意,线程池可以自由地重复使用相同的线程来对其运行continuation,因此不能保证它会有所不同:

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: 您可以检查的另一件事是ThreadPool.GetAvailableThreads以确定是否已ThreadPool.GetAvailableThreads另一名工作人员使用:

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

There is a major mistake in your assumption: 你的假设有一个重大错误:

When it hits await Task.Delay(5), thread 2000 returns to the thread pool 当它命中等待Task.Delay(5)时,线程2000返回到线程池

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; ). 由于你还没有等待foo() ,当线程2000命中Task.Delay(5)它只是创建一个新的Task并返回testAsync() (到int i = 0; )。 It moves on to the while block, and only then you await task . 它移动到while块,然后你等待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. 此时,如果task尚未完成,并且假设等待其余代码,则线程2000将返回到线程池。 Otherwise, if task is already completed, it will synchronously continue from foo() (at while (y<5) y++; ). 否则,如果task已经完成,它将同步从foo()继续(at while (y<5) y++; )。

EDIT: 编辑:

what if the main method called testAsync? 如果主要方法叫做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. 请注意,在上面的情况下,线程没有返回到线程池 - 它完全被OS挂起。

Maybe you can give an example of how to call testAsync so that thread 2000 returns to the thread pool? 也许您可以举例说明如何调用testAsync以便线程2000返回到线程池?

Assuming thread 2k is the main thread, it cannot return to the thread pool. 假设线程2k是主线程,它不能返回线程池。 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. 但是你可以使用Task.Run(()=> foo())在线程池上运行foo() ,并且由于调用线程是主线程,另一个线程池线程将获取该任务。 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. 线程3和4来自并返回到线程池。

I'd like to verify which one is really happening. 我想验证哪一个真的发生了。

There is no way to "verify" that with debugger, because the debugger is made to simulate the logical (synchronous) flow - see Walkthrough: Using the Debugger with Async Methods . 没有办法用调试器“验证”它,因为调试器用于模拟逻辑(同步)流 - 请参阅演练:使用调试器和异步方法

In order to understand what is happening (FYI it's your case (2)), you need to learn how await works starting from Asynchronous Programming with Async and Await - What Happens in an Async Method section, Control Flow in Async Programs and many other sources. 为了理解正在发生的事情(仅供你参考(2)),你需要了解await是如何工作的,从Async和Await的异步编程开始 - 异步方法部分发生了什么 ,异步程序中的控制流和许多其他来源。

Look at this snippet: 看看这个片段:

static void Main(string[] args)
{
    Task.Run(() =>
    {
        // Initial thread pool thread
        var t = testAsync();
        t.Wait();
    });
    Console.ReadLine();
}

If we make the lambda to be async and use await t; 如果我们使lambda为async并使用await t; instead of t.Wait(); 而不是t.Wait(); , this is the point where the initial thread will be returned to the thread pool. ,这是初始线程将返回到线程池的点。 As I mentioned above, you cannot verify that with debugger. 如上所述,您无法使用调试器验证。 But look at the above code and think logically - we are blocking the initial thread, so if it' wasn't free, your testAsync and foo methods will not be able to resume. 但是看看上面的代码并从逻辑上思考 - 我们阻止了初始线程,所以如果它不是免费的,那么你的testAsyncfoo方法将无法恢复。 But they do, and this can easily be verified by putting breakpoint after await lines. 但是他们这样做了, 很容易通过 await线之后放置断点来验证。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM