[英]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上运行。有两种可能的情况:
当它命中await Task.Delay(5)
,线程2000返回线程池很短的时间,并且控制返回到调用者,在第(2)行,它将在从线程池获取的线程2000上执行。 如果这是真的,则无法轻松检测到它,因为Thread 2000 在两个连续代码行之间的时间段内位于线程池中。
当它命中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();
,这是初始线程将返回到线程池的点。 如上所述,您无法使用调试器验证。 但是看看上面的代码并从逻辑上思考 - 我们阻止了初始线程,所以如果它不是免费的,那么你的testAsync
和foo
方法将无法恢复。 但是他们这样做了, 这很容易通过在 await
线之后放置断点来验证。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.