WebRequest';除非调用线程空闲(方法已完成),否则不会调用s回调

本文关键字:调用 已完成 回调 方法 线程 WebRequest | 更新日期: 2023-09-27 17:58:13

这是我的代码(它是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

重点是回拨电话。

提前感谢

WebRequest';除非调用线程空闲(方法已完成),否则不会调用s回调

回调的意义在于,只有在您的请求完成并收到响应后才能调用它。

这似乎需要2秒钟以上的时间,因此线程结束消息发生在回调之前。

此外,Outer thread end在2秒后立即写入,而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消息通常会在它之前,但对于异步处理,您不应该依赖它。

如果这一点不清楚,请在评论中告诉我,我将尝试进一步说明。

编辑

现在我看到您正在使用Silverlight,问题变得越来越清楚。我认为,如果您真正调用EndGetResponse(),您最终会得到SecurityException,因为Silverlight希望在默认情况下阻止跨站点脚本攻击。也就是说,如果没有建立允许的策略,您就无法访问Silverlight应用程序所在域(如microsoft.com或google.com)以外的域中的URL。

这里详细介绍了安全策略:http://msdn.microsoft.com/en-us/library/cc645032(v=VS.95).aspx

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应用程序的本地机器。消息框显示开始,运行调用,内部,ok,然后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"));
        }

您不能期望在显示"外端"之前显示任何消息框,因为它相当于调用

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。您不能期望在"一个结束"之前显示"两个",它不能在第一个排队委托的执行过程中注入。