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

时间:2015-11-15 09:25:25

标签: c# multithreading debugging async-await

如何使用VS C#2015调试器检查线程是否返回到线程池?

在我的情况下,有什么问题是无法通过逐行调试来检测它。

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;
}

在线程2000上运行的testAsync的第一行中,调用foo。遇到await Task.Delay(5)后,线程2000返回到线程池(据称,我试图确认这一点),该方法等待Task.Delay(5)完成。在此期间,控件返回给调用者,并且testAsync的第一个循环也在线程2000上执行。

因此,在两个连续的代码行之间,线程返回到线程池并从那里返回。如何使用调试器确认?可能使用Threads调试器窗口?

为了澄清我的问题:foo正在线程2000上运行。有两种可能的情况:

  1. 当它命中await Task.Delay(5)时,线程2000返回线程池很短的时间,控件返回到调用者,在第(2)行,它将在线程2000上执行线程池。如果这是真的,那么你就无法轻易检测到它,因为Thread 2000在两个连续代码行之间的时间位于线程池中。

  2. 当它命中await Task.Delay(5)时,线程2000不会返回线程池,但会立即从第(2)行开始执行testAsync中的代码

  3. 我想验证哪一个确实在发生。

2 个答案:

答案 0 :(得分:3)

您可以将Thread.CurrentThread.ManagedThreadId打印到控制台。请注意,线程池可以自由地重复使用相同的线程来运行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 }");
}

您可以检查的另一件事是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;
}

在我的机器上,这会产生:

Thread-Pool threads before first await:
Available Workers: 1023, Available IO: 1000
----------------------------------------------
Thread-Pool threads after first await:
Available Workers: 1022, Available IO: 1000

答案 1 :(得分:1)

  

我想验证哪一个确实在发生。

没有办法&#34;验证&#34;使用调试器,因为调试器用于模拟逻辑(同步)流 - 请参阅Walkthrough: Using the Debugger with Async Methods

为了理解正在发生的事情(仅供参考,你的情况(2)),你需要了解awaitAsynchronous Programming with Async and Await开始的工作方式 - 异步方法部分,Control Flow in Async Programs和许多其他来源。

请看这个片段:

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

如果我们将lambda设为async并使用await t;而不是t.Wait();,那么这就是初始线程将返回到线程池的点。如上所述,您无法使用调试器验证。但是看看上面的代码并从逻辑上思考 - 我们阻止了初始线程,所以如果它&#39;没有免费,您的testAsyncfoo方法将无法恢复。但他们确实可以通过在 await行之后设置断点来轻松验证