WebRequest зависает в пользовательском интерфейсе

Я делаю простой запрос с помощью WebRequest, и приложение просто зависает, пока не вернется ответ. Как я могу это исправить?

Я прочитал много тем, и все они говорят, что нужно использовать темы. Я не знаю, как их использовать; Может ли кто-нибудь привести пример следующего, который не зависает в пользовательском интерфейсе?

private string SimpleRequest(String url)
{
    WebRequest request = WebRequest.Create(url);
    WebResponse response = request.GetResponse();
    Stream stream = response.GetResponseStream();
    StreamReader reader = new StreamReader(stream);
    string result = reader.ReadToEnd();
    stream.Dispose();
    reader.Dispose();
    return result;
}

private void button1_Click(object sender, EventArgs e)
{
    String googleHtml = simpleRequest("https://facebook.com");
}

Спасибо!


person user1558223    schedule 26.08.2012    source источник


Ответы (4)


Вы можете использовать BackgroundWorker, чтобы использовать другой поток, кроме потока пользовательского интерфейса. Вот пример.

Вы также можете отменить или отобразить прогресс:

public partial class fmMain : Form
{
    private void btnrunBackgroundWorker_Click( object sender, EventArgs e )
    {
        // Create a background worker
        BackgroundWorker bw = new BackgroundWorker();
        bw.DoWork += new DoWorkEventHandler( bw_DoWork );

        // run in another thread
        bw.RunWorkerAsync();     
    }

    private void bw_DoWork( object sender, DoWorkEventArgs e )
    {
        String facebookHtml = simpleRequest("https://facebook.com");
    }
}
person Hassan Boutougha    schedule 26.08.2012
comment
Кстати, быстрый вопрос: если я хочу использовать его в цикле for, я просто добавляю в цикл Thread.Sleep( 100 );? - person user1558223; 26.08.2012
comment
если вы хотите сделать много запросов (много вызовов simpleRequest) без ожидания завершения, вы можете сделать асинхронный вызов вашего simpleRequest, но если вы хотите сделать много вызовов с ожиданием завершения, вы можете использовать обратный вызов, чтобы получить завершение, и в этом, чтобы начать другой вызов SimpleRequest (ожидание с Thread.Sleep в определенное время - хороший способ ;-) - person Hassan Boutougha; 26.08.2012

Если вы используете С# 5.0, это слишком просто

public async void Test1()
{
      WebClient wc = new WebClient();
      richTextBox1.Text =  await wc.DownloadStringTaskAsync("https://facebook.com");
}

Вы также можете преобразовать свой метод в awaitable

private Task<string> simpleRequest(String url)
{
    return Task.Factory.StartNew(() =>
        {
            WebRequest request = WebRequest.Create(url);
            WebResponse response = request.GetResponse();
            Stream stream = response.GetResponseStream();
            StreamReader reader = new StreamReader(stream);
            string result = reader.ReadToEnd();
            stream.Dispose();
            reader.Dispose();
            return result;
        });
}

и позвони как

public async void Test2()
{
      richTextBox1.Text =  await simpleRequest("https://facebook.com");
}

Для более низких версий С# см. другие ответы.

person L.B    schedule 26.08.2012
comment
танки, лучший ответ - person sirmagid; 30.08.2017

Вы делаете доступ к сети в основном потоке пользовательского интерфейса. Пользовательский интерфейс имеет основной поток, который зацикливается и обновляет пользовательский интерфейс. Если вы сделаете доступ к сети в этом потоке, пользовательский интерфейс не будет обновляться, и он будет зависать до тех пор, пока ваш метод не вернется и цикл не сможет продолжиться.

Рассмотрите возможность выполнения своей работы в фоновом потоке. Одним из простых способов сделать это в .net является класс BackgroundWorker.

В официальной документации есть хороший пример его использования:

http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

Класс BackgroundWorker позволяет выполнять операцию в отдельном выделенном потоке. Отнимающие много времени операции, такие как загрузка и транзакции базы данных, могут привести к тому, что ваш пользовательский интерфейс (UI) перестанет отвечать на запросы во время их работы. Если вам нужен отзывчивый пользовательский интерфейс и вы сталкиваетесь с длительными задержками, связанными с такими операциями, класс BackgroundWorker предоставляет удобное решение.

person bryanmac    schedule 26.08.2012

Вы должны использовать BackgroundWorker чтобы ваш основной поток пользовательского интерфейса не зависал!

private void button1_Click(object sender, EventArgs e)
{
    backgroundWorker1.RunWorkerAsync("https://facebook.com"); // start async operation
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    e.Result = simpleRequest(e.Argument.ToString()); // set result in async operation
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Error == null)
    {
        // show result here
        // or access/manage result here
        MessageBox.Show(e.Result.ToString());
    }
    else
    {
        // manage/show error
        MessageBox.Show(e.Error.Message);
    }
}
private String simpleRequest(String url)
{
     // your code goes here
}

Вы также можете реализовать backgroundWorker1_ProgressChanged, чтобы пользователь мог видеть ход загрузки веб-сайта. (sideNote: см. этот вопрос для получения подробной информации о 12121804">ProgressChanged и контекст)

person Pilgerstorfer Franz    schedule 26.08.2012
comment
вы получите строку результата с помощью e.Result.ToString(), как показано в моем примере: D Вы можете сами решить, что с ней делать, см. комментарии в RunWorkerCompletet - person Pilgerstorfer Franz; 26.08.2012
comment
я сделал это, я пытался вывести его, но там ничего нет, он не запускает backgroundWorker1_RunWorkerCompleted... - person user1558223; 26.08.2012
comment
возможно, у вас возникла ошибка, попробуйте показать любое сообщение об ошибке - я обновлю свой ответ - person Pilgerstorfer Franz; 26.08.2012
comment
:(( почему у меня это не работает, вот полный код, пожалуйста, проверьте pastebin.com/embed_js .php?i=2EZeUK4S - person user1558223; 26.08.2012
comment
обновите свой код, чтобы отображать любые сообщения об ошибках, как показано в моем обновленном ответе - возможно, ваш брандмауэр блокирует или что-то еще - MessageBox.Show(e.Error.Message); - person Pilgerstorfer Franz; 26.08.2012
comment
нет, ничего :(, кстати, я должен добавить это к кнопке справа BackgroundWorker backgroundWorker1 = new BackgroundWorker();? также, если вы не возражаете, сохраните проект и загрузите его - person user1558223; 26.08.2012