【问题标题】:WebRequest's callback doesnt get called unless calling thread is free (method is finished)除非调用线程空闲(方法已完成),否则不会调用 Web 请求回调
【发布时间】:2011-09-06 06:03:28
【问题描述】:

这是我的代码(它是 Silverlight!):

public class ThreadTester
{
    public void Test()
    {
        Debug.WriteLine("Outer thread start");
        ThreadPool.QueueUserWorkItem(x => RunInner());
        Thread.Sleep(2000);
        Debug.WriteLine("Outer thread end");
    }

    private void RunInner()
    {
        Debug.WriteLine("Inner thread start");

        BL bl = new BL();
        bl.Run1(AssyncCallback);

        Debug.WriteLine("Inner thread end");
    }

    public void AssyncCallback(IAsyncResult ar)
    {
        Debug.WriteLine("Async Callback called!");
    }

}

public class BL
{
    public void Run1(AsyncCallback callback)
    {
        WebRequest req = WebRequest.Create(@"http://microsoft.com");
        req.BeginGetResponse(callback, null);
    }
}

这是我在输出窗口中得到的:

Outer thread start
Inner thread start
Outer thread end
Inner thread end
Async Callback called!

任何想法为什么它会这样工作?不应该吗

Outer thread start
Inner thread start
Async Callback called!
Inner thread end
Outer thread end

主要强调回调调用。

提前致谢

【问题讨论】:

    标签: c# silverlight asynchronous callback webrequest


    【解决方案1】:

    回调的重点是,它只有在您的请求完成并收到您的响应后才能调用。

    这似乎需要超过 2 秒,因此线程结束消息发生在回调之前。

    此外,Outer thread end 会在 2 秒结束后立即写入,而 within RunInner() 的所有内容都是同步发生的,因此 Inner thread end 直到请求结束后才会显示完成(但在回调执行之前,因为您使用的是异步BeginGetResponse)。

    当我运行相同的代码时,我实际上得到了这个输出:

    Outer thread start
    Inner thread start
    Inner thread end
    Async Callback called!
    Outer thread end
    

    这只是意味着我的请求不到 2 秒。如果您将Thread.Sleep() 值增加几秒钟,您应该会得到相同的结果。不过,回调再次被异步调用,因此 Inner thread end 消息通常会在它之前,但对于异步处理,您不应该依赖它。

    如果不清楚,请在 cmets 中告诉我,我会尝试进一步说明。

    编辑

    现在我看到您正在使用 Silverlight,问题变得更加清晰。我认为,如果您通过实际调用EndGetResponse(),您最终会得到SecurityException,因为默认情况下,Silverlight 想要阻止跨站点脚本攻击。也就是说,如果没有制定允许的策略,您将无法访问与 Silverlight 应用程序所在域不同的域(如 microsoft.com 或 google.com)中的 URL。

    这里有对安全策略的详细处理:http://msdn.microsoft.com/en-us/library/cc645032(v=VS.95).aspx

    【讨论】:

    • 好的,我将等待时间更改为 10 秒,输出相同。所以你关于它需要超过 2 秒的想法是行不通的。
    • @deafsheep 在浏览器中打开 microsoft.com 需要多少秒?您为什么不尝试更轻量级的网站,例如 google.com?
    • 可能需要 1-2 秒(包括所有图像、重定向等)。不是那样,我告诉你,尝试 google.com - 都一样。似乎有些东西正在阻止请求。有什么想法吗?
    • 我很抱歉杰,这一切都发生在 silverlight 内(忘了提),在按钮点击事件上(ThreadTester.Test() 被调用)
    • @deafsheep 好的,我从控制台应用程序调用它。今晚我会尝试进一步调查(UTC -5:00);抱歉,我不能早点完成。
    【解决方案2】:
    public partial class MainPage : UserControl
        {
            private WebRequest m_request;
            public MainPage()
            {
                InitializeComponent();
                MessageBox.Show("began!");
                ThreadPool.QueueUserWorkItem(o => Run1(AssyncCallback));
    
            }
    
            public void Run1(AsyncCallback callback)
            {
                Deployment.Current.Dispatcher.BeginInvoke(() => MessageBox.Show("run called"));
                m_request = WebRequest.Create(@"http://localhost:9892/testGet.htm");
                m_request.BeginGetResponse(callback, null);
                Thread.Sleep(5000);
                Deployment.Current.Dispatcher.BeginInvoke(() => MessageBox.Show("finish"));
            }
    
            public void AssyncCallback(IAsyncResult ar)
            {
                Deployment.Current.Dispatcher.BeginInvoke(() => MessageBox.Show("inside"));
                try {
                    m_request.EndGetResponse(ar);
                    Deployment.Current.Dispatcher.BeginInvoke(() => MessageBox.Show("ok"));
    
                }
                catch (Exception e) {
                    Deployment.Current.Dispatcher.BeginInvoke(() => MessageBox.Show(e.Message));
                    throw;
                }
            }
        }
    

    在我的本地机器上运行良好,其中 testGet.htm 是 SL 应用程序的本地机器。 消息框显示开始,运行调用,里面,好的,然后 5 秒后完成

    编辑:

    如果我们像您建议的那样进行以下代码更改,那么您在理论上的建议可能行不通:

    public MainPage()
            {
                InitializeComponent();
                MessageBox.Show("began!");
                ThreadPool.QueueUserWorkItem(o => Run1(AssyncCallback));
                Thread.Sleep(5000);
                MessageBox.Show("outer ended")
            }
    
            public void Run1(AsyncCallback callback)
            {
                Deployment.Current.Dispatcher.BeginInvoke(() => MessageBox.Show("run called"));
                m_request = WebRequest.Create(@"http://localhost:9892/testGet.htm");
                m_request.BeginGetResponse(callback, null);
                Deployment.Current.Dispatcher.BeginInvoke(() => MessageBox.Show("finish"));
            }
    

    你不能期望在显示“outerended”之前显示任何消息框,因为它相当于调用

    ThreadPool.QueueUserWorkItem(o =>
                    {
                        MessageBox.Show("one");
                        Thread.Sleep(5000);
                        MessageBox.Show("one ended");
                    });
                ThreadPool.QueueUserWorkItem(o => MessageBox.Show("two"));
                ThreadPool.QueueUserWorkItem(o => MessageBox.Show("three"));
    

    如果 ThreadPool MaxThreads 为 1。你不能指望“两个”会在“一个结束”之前显示,它不能在第一个排队委托的执行过程中注入。

    【讨论】:

    • 虽然该解决方案确实有效,但它并没有捕捉到原始问题的想法 - 等待原始 UI 线程中的异步调用结束。如果您从 Run1 中删除等待,并将其放在 MainPage() 中的 ThreadPool.QueueUserWorkItem(o => Run1(AssyncCallback));并放置像 MessageBox.Show("outerended!"); 这样的消息框睡眠后,理论上你应该看到“外结束!”消息框最后出现,但它从来不会这样,它总是首先显示。
    • 那个“理论”不符合 UI 线程的工作方式。在 UI 线程中运行的方法必须在其他方法可以在 UI 上调用 MessageBox.Show 之前返回,在其未完成之前,他们所做的只是将调用放入队列中以在 UI 线程上执行,就像 ThreadPool.QueueUserWorkItem 会在线程池有 1 个线程时工作在里面。
    • 是的,你是对的,我们将 UI 线程休眠 5 秒后,“外部结束”将被延迟,但这并不能解决问题:在 UI 方法之前不会调用回调完成(即 UI 线程是空闲的)。消息框的顺序显示(回调总是最后)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多