ASP.NET MVC 中未调用 WebClient 异步回调

发布于 2024-11-02 10:38:18 字数 2022 浏览 0 评论 0原文

在 GET 请求中,我运行(类似于):

public ActionResult Index(void) {
    webClient.DownloadStringComplete += onComplete;
    webClient.DownloadStringAsync(...);
    return null;
}

我看到 onCompleteIndex() 完成执行之前不会被调用。 我可以看到 onComplete 是在与执行 Index 不同的线程上调用的。

问题:为什么会发生这种情况?为什么 webClient 的异步线程明显被阻塞,直到请求处理线程完成?

有没有办法解决这个问题,而无需从 ThreadPool 启动新线程(我尝试过,使用线程池确实可以按预期工作。如果从 ThreadPool 的线程调用 DownloadStringAsync,webClient 的回调也会按预期发生) 。

ASP.NET MVC 3.0、.NET 4.0、MS Cassini 开发 Web 服务器(VS 2010)

编辑: 这是完整的代码:

public class HomeController : Controller {
    private static ManualResetEvent done;

    public ActionResult Index() {
        return Content(DownloadString() ? "success" : "failure");
    }

    private static bool DownloadString() {
        try {
            done = new ManualResetEvent(false);
            var wc = new WebClient();
            wc.DownloadStringCompleted += (sender, args) => { 
                // this breakpoint is not hit until after Index() returns.
                // It is weird though, because response isn't returned to the client (browser) until this callback finishes.
                // Note: This thread is different from one Index() was running on.
                done.Set(); 
            };

            var uri = new Uri(@"http://us.battle.net/wow/en/character/blackrock/hunt/simple");

            wc.DownloadStringAsync(uri);

            var timedout = !done.WaitOne(3000);
            if (timedout) {
                wc.CancelAsync();
                // if this would be .WaitOne() instead then deadlock occurs.
                var timedout2 = !done.WaitOne(3000); 
                Console.WriteLine(timedout2);
                return !timedout2;
            }
            return true;
        }
        catch (Exception ex) {
            Console.WriteLine(ex.Message);
        }
        return false;
    }
}

On GET request I run (something like):

public ActionResult Index(void) {
    webClient.DownloadStringComplete += onComplete;
    webClient.DownloadStringAsync(...);
    return null;
}

I see that onComplete isn't get invoked until after Index() has finished execution.
I can see that onComplete is invoked on a different thread from one Index was executed on.

Question: why is this happening? why is webClient's async thread is apparently blocked until request handling thread is finished?

Is there a way to fix this without starting new thread from ThreadPool (I tried this, and using thread pool does work as expected. Also webClient's callback does happen as expected if DownloadStringAsync is called from a ThreadPool's thread).

ASP.NET MVC 3.0, .NET 4.0, MS Cassini dev web server (VS 2010)

EDIT: Here is a full code:

public class HomeController : Controller {
    private static ManualResetEvent done;

    public ActionResult Index() {
        return Content(DownloadString() ? "success" : "failure");
    }

    private static bool DownloadString() {
        try {
            done = new ManualResetEvent(false);
            var wc = new WebClient();
            wc.DownloadStringCompleted += (sender, args) => { 
                // this breakpoint is not hit until after Index() returns.
                // It is weird though, because response isn't returned to the client (browser) until this callback finishes.
                // Note: This thread is different from one Index() was running on.
                done.Set(); 
            };

            var uri = new Uri(@"http://us.battle.net/wow/en/character/blackrock/hunt/simple");

            wc.DownloadStringAsync(uri);

            var timedout = !done.WaitOne(3000);
            if (timedout) {
                wc.CancelAsync();
                // if this would be .WaitOne() instead then deadlock occurs.
                var timedout2 = !done.WaitOne(3000); 
                Console.WriteLine(timedout2);
                return !timedout2;
            }
            return true;
        }
        catch (Exception ex) {
            Console.WriteLine(ex.Message);
        }
        return false;
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

随心而道 2024-11-09 10:38:18

我对此很好奇,所以我询问了 Microsoft 内部 ASP.NET 讨论别名,并得到了 Levi Broderick 的回复:

ASP.NET 内部使用
同步上下文
同步,并且只有一个线程
一次被允许有
控制该锁。在你的
具体的例子,线程运行
HomeController::DownloadString 保持
锁,但它正在等待
要触发的 ManualResetEvent。这
ManualResetEvent 不会被触发,直到
DownloadStringCompleted 方法
运行,但该方法运行在
永远无法接受的不同线程
同步锁是因为
第一个线程仍然保留它。你是
现在陷入僵局。

我很惊讶这竟然在
MVC 2,但如果它做到了,那也只是通过
快乐的意外。这从来都不是
支持。

I was curious about this so I asked on the Microsoft internal ASP.NET discussion alias, and got this response from Levi Broderick:

ASP.NET internally uses the
SynchronizationContext for
synchronization, and only one thread
at a time is ever allowed to have
control of that lock. In your
particular example, the thread running
HomeController::DownloadString holds
the lock, but it’s waiting for the
ManualResetEvent to be fired. The
ManualResetEvent won’t be fired until
the DownloadStringCompleted method
runs, but that method runs on a
different thread that can’t ever take
the synchronization lock because the
first thread still holds it. You’re
now deadlocked.

I’m surprised that this ever worked in
MVC 2, but if it did it was only by
happy accident. This was never
supported.

攀登最高峰 2024-11-09 10:38:18

这就是使用异步处理的要点。您的主线程开始调用,然后继续执行其他有用的操作。当调用完成时,它从 IO 完成线程池中选择一个线程,并在其上调用您注册的回调方法(在本例中是您的 onComplete 方法)。这样,您就不需要使用昂贵的线程来等待长时间运行的 Web 调用完成。

无论如何,您使用的方法遵循基于事件的异步模式。您可以在这里阅读更多相关信息:http://msdn.microsoft.com/en -us/library/wewwczdw.aspx

(编辑)注意:忽略此答案,因为它无助于回答已澄清的问题。将其留待其下进行的讨论。

This is the point of using asynchronous processing. Your main thread starts the call, then goes on to do other useful things. When the call is complete, it picks a thread from the IO completion thread pool and calls your registered callback method on it (in this case your onComplete method). That way you don't need to have an expensive thread waiting around for a long-running web call to complete.

Anyway, the methods you're using follow the Event-based Asynchronous Pattern. You can read more about it here: http://msdn.microsoft.com/en-us/library/wewwczdw.aspx

(edit) Note: Disregard this answer as it does not help answer the clarified question. Leaving it up for the discussion that happened under it.

一场信仰旅途 2024-11-09 10:38:18

除了所选答案之外,请参阅本文以获取有关 WebClient 捕获 SynchronizationContext 原因的更多详细信息。

http://msdn.microsoft.com/en-gb/magazine/gg598924.aspx

In addition to the chosen answer, see this article for further details on why the WebClient captures the SynchronizationContext.

http://msdn.microsoft.com/en-gb/magazine/gg598924.aspx

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文