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

时间:2011-06-01 12:10:22

标签: c# silverlight asynchronous callback webrequest

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

主要强调回叫电话。

提前致谢

2 个答案:

答案 0 :(得分:0)

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

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

此外,{2}启动时会Outer thread end写入,而 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

答案 1 :(得分:0)

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"));
        }

在显示“外部结束”之前,您不能指望显示任何消息框,因为它等同于调用

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