Обратный вызов WebRequest не вызывается, если вызывающий поток не является бесплатным (метод завершен) - PullRequest
2 голосов
/ 01 июня 2011

Вот мой код (это 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 голосов
/ 06 июня 2011
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 секунд заканчиваются

Edit:

То, что вы предлагаете в теории, может не сработать, если у нас будет следующее изменение кода, как вы предлагаете:

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. Вы не можете ожидать, что «два» будет отображаться до того, как «один закончился», он не может внедрить его в середине выполнения первого делегата из очереди.

0 голосов
/ 01 июня 2011

Смысл обратного вызова заключается в том, что его можно вызывать только после того, как ваш запрос будет выполнен и ваш ответ получен.

Похоже, что это занимает более 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 обычно предшествует ему, но при асинхронной обработке вы не должны полагаться на это.

Дайте мне знать в комментариях, если это не ясно, и я попытаюсь проиллюстрировать это дальше.

1024 * редактировать * Теперь, когда я вижу, что вы используете Silverlight, проблемы проясняются. Я думаю, что если вы перейдете к фактическому вызову EndGetResponse(), вы получите SecurityException, потому что Silverlight по умолчанию хочет блокировать атаки межсайтового скриптинга. То есть вы не можете получить доступ к URL-адресам в доменах, отличных от тех, в которых живет ваше приложение Silverlight (например, microsoft.com или google.com), не установив политику, которая это позволяет. Здесь подробно рассматривается политика безопасности: http://msdn.microsoft.com/en-us/library/cc645032(v=VS.95).aspx

...