为什么我的异步回调在同一个线程中运行?

4
我想使用 FtpWebRequest 的异步调用(BeginGetResponse / EndGetResponse)。
然而,似乎来自 BeginGetResponse 的回调在与我的应用程序相同的线程中运行,而我以为它将使用不同的(和线程池的)线程。这意味着我的应用程序在回调中被阻塞,无法继续运行。
我已经按照以下方式设置了一个LINQPad概念验证:
"Starting".Dump();
Thread.CurrentThread.GetHashCode().Dump(); 
Thread.CurrentThread.IsThreadPoolThread.Dump();

IAsyncResult result = request.BeginGetResponse((ar) => 
{
     "Inside Callback".Dump();
     Thread.CurrentThread.GetHashCode().Dump();
     Thread.CurrentThread.IsThreadPoolThread.Dump();
     var resp = request.EndGetResponse(ar);
     "Callback Complete".Dump();
}, null);

"After Callback".Dump();

这将输出以下内容:

Starting
33
False
Inside Callback
33
False
Callback Complete
After Callback

我所期望的是类似于以下内容(假设回调函数运行时间足够长):
Starting
33
False
Inside Callback
44
True
After Callback // App continues despite callback running
Callback Complete

回调函数在同一应用程序线程上运行,这意味着如果回调函数内部的某些操作需要很长时间(例如,为了举例而引入一个Thread.Sleep),我的应用程序会在那里阻塞。这意味着我无法设置请求的超时时间(例如,使用ThreadPool.RegisterWaitForSingleObject)。

我有遗漏什么吗?


你所忽略的是回调函数只有在请求完成后才会被调用。异步请求的目的是确保请求本身不会阻塞你的线程。 - dlev
好的,但如果请求是异步的,为什么回调函数不在异步线程池线程中?假设请求花费了很长时间(且是异步的),并且应用程序线程已经继续执行,这是否意味着回调函数可以在任何后续时间点阻塞应用程序线程?我会尝试设置一个缓慢的服务器并查找答案。 - Matt Mitchell
3
回调函数中 ar.CompletedSynchronously 的值是什么? - dtb
@dtb ar.CompletedSynchronouslytrue - Matt Mitchell
@JeffMercado 是这样的。你愿意在 @dtb 发布吗? - Matt Mitchell
2
此外,大多数情况下与此无关,但您不应该依赖于GetHashCode来完成您正在进行的操作;它不能保证在线程之间是唯一的。对于这个问题,有一个ManagedThreadId可以使用。 - Michael Edenfield
2个回答

5

我也感到足够谨慎,现在去检查一下我的其他调用代码(其中我正在使用线程kill超时调用一个操作),以确保它没有在同一个线程中运行。 - Matt Mitchell

0

我假设BeginGetResponse方法在调用回调时处于调用线程的上下文中


我也是这么认为的,但这与我看到的文档相矛盾,而且当异步调用的回调可以在未来的任何时间阻塞应用程序线程时,这有点违背了异步调用的目的。我想我需要在我的回调函数中嵌套一个“BeginInvoke”。 - Matt Mitchell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接