繁体   English   中英

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

[英]Checking if a thread returned to thread pool

如何使用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循环testAsync线程2000上执行。

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

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

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

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

我想验证哪一个真的发生了。

您可以将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以确定是否已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

你的假设有一个重大错误:

当它命中等待Task.Delay(5)时,线程2000返回到线程池

由于你还没有等待foo() ,当线程2000命中Task.Delay(5)它只是创建一个新的Task并返回testAsync() (到int i = 0; )。 它移动到while块,然后你等待task 此时,如果task尚未完成,并且假设等待其余代码,则线程2000将返回到线程池。 否则,如果task已经完成,它将同步从foo()继续(at while (y<5) y++; )。

编辑:

如果主要方法叫做testAsync怎么办?

当同步方法调用并等待异步方法时,如果异步方法返回未完成的任务,它必须阻塞该线程:

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

请注意,在上面的情况下,线程没有返回到线程池 - 它完全被OS挂起。

也许您可以举例说明如何调用testAsync以便线程2000返回到线程池?

假设线程2k是主线程,它不能返回线程池。 但是你可以使用Task.Run(()=> foo())在线程池上运行foo() ,并且由于调用线程是主线程,另一个线程池线程将获取该任务。 所以下面的代码:

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

制作(在我的电脑上)以下输出:

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

线程3和4来自并返回到线程池。

我想验证哪一个真的发生了。

没有办法用调试器“验证”它,因为调试器用于模拟逻辑(同步)流 - 请参阅演练:使用调试器和异步方法

为了理解正在发生的事情(仅供你参考(2)),你需要了解await是如何工作的,从Async和Await的异步编程开始 - 异步方法部分发生了什么 ,异步程序中的控制流和许多其他来源。

看看这个片段:

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

如果我们使lambda为async并使用await t; 而不是t.Wait(); ,这是初始线程将返回到线程池的点。 如上所述,您无法使用调试器验证。 但是看看上面的代码并从逻辑上思考 - 我们阻止了初始线程,所以如果它不是免费的,那么你的testAsyncfoo方法将无法恢复。 但是他们这样做了, 很容易通过 await线之后放置断点来验证。

暂无
暂无

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

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