2015-12-20 5 views
0

кодаC# задачи и методы недействительных

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using HtmlAgilityPack; 
using System.Windows.Forms; 

namespace WindowsFormsApplication1 
{ 
    public partial class Form1 : Form 
    { 
     public Form1() 
     { 
      InitializeComponent(); 
      textBox1.Text = "place url hear"; 

     } 

     private void Form1_Load(object sender, EventArgs e) 
     { 

     } 

     private void textBox1_TextChanged(object sender, EventArgs e) 
     { 

     } 

     private void button1_Click(object sender, EventArgs e) 
     { 

      Task.Factory.StartNew(() => get_url_contents(textBox1.Text)).ContinueWith(t => t.Id, TaskScheduler.FromCurrentSynchronizationContext()); 

     } 


     private void get_url_contents(string url) 
     { 
      var doc = new HtmlWeb().Load(url); 

      HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//a"); 


      foreach(HtmlNode node in nodes) 
      { 
       listView1.Items.Add(node.InnerText); 
      } 

     } 
     private void listView1_SelectedIndexChanged(object sender, EventArgs e) 
     { 

     } 
    } 
} 

Im с помощью окна формы, и я практикуя C#, Im довольно новый для этого языка, но знает немного питона.

В основном, что я пытаюсь сделать, вы набираете url на textBox1, и когда вы нажимаете button1, он перейдет к этому URL-адресу и извлечет весь текст ссылки.

и append эти результаты listView1 однако я получаю эту ошибку

сообщение об ошибке:

Additional information: Cross-thread operation not valid: Control 'listView1' accessed from a thread other than the thread it was created on.

как мы это исправить?

+0

Я получил это для работы. сделав еще один метод, который возвращает что-то, а затем запустить его на 'метод ContinueWith' как это: ' ContinueWith (т => other_method_that_processes_results (t.Result), ..........) ' Однако я хотел бы просто сделать все это одним способом. но, видимо, это недопустимо – Zion

+0

Правильное решение было предоставлено слишком много раз на SO. Давайте притворимся, что OP уже искали и не обнаружили, что это не интересно ... Или просто downvote по вопросу ... –

ответ

8

К сожалению, принятый ответ, к сожалению, продолжает идти по неправильному пути, в котором вы оказались в первую очередь. Проблема здесь в том, что «мне пришлось обновить пользовательский интерфейс из неправильного потока, выполнив асинхронную операцию над рабочим потоком». Приведенное решение: «иметь рабочий поток, маршал, вызов обратно в поток пользовательского интерфейса». Лучшее решение не в конечном итоге пытается сделать работу с пользовательским интерфейсом на рабочем потоке.

Также нет необходимости гадать с ContinueWith; в C# 5 и выше мы имеем асинхронное ожидание.

Предположим, что у нас действительно есть работа, которую мы хотим выполнить в другом потоке. (Это подозрительно, нет причин, по которым операция с высокой задержкой здесь должна идти по другому потоку. Это не процессор, связанный! Но для аргумента предположим, что мы хотим загрузить HTML-код в другой поток:

async private void button1_Click(object sender, EventArgs e) 
    { 

Обратите внимание, что я отметил метод async.Это не заставляет его работать в другом потоке. Это означает, что «этот метод вернется к своему вызывающему абоненту - цикл сообщения, отправивший событие - до работы метод делается. Это резюме внутри метода в какой-то момент в будущем.»

 var doc = await Task.Factory.StartNew(() => new HtmlWeb().Load(url)); 

Что мы имеем? мы порождает асинхронную задачу, которая загружает некоторый HTML и возвращает задачу. Затем мы ожидаем эту задачу. Ожидание задачи означает «сразу же вернуться к моему вызывающему абоненту - цикл сообщений, который отправил кнопку« щелчок »- чтобы пользовательский интерфейс выполнялся. Когда задача завершается, этот метод возобновится здесь и получит значение, вычисленное заданием. "

Теперь остальная часть программы совершенно нормально:

 HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//a"); 
     foreach(HtmlNode node in nodes) 
     { 
      listView1.Items.Add(node.InnerText); 
     } 
    } 

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

Теперь здесь есть несколько проблем.

Что произойдет, если кнопка будет нажата снова, пока мы будем ждать загрузки HTML? Мы пытаемся загрузить его снова! Было бы неплохо выключить кнопку перед ожиданием и снова вернуться после.

Кроме того, как я упоминал ранее, почему мы порождаем поток для сетевой операции? Вы хотите отправить письмо своей тете и получить ответ; вам не нужно нанимать работника, чтобы взять письмо в почтовый ящик, отправить его по почте, а затем сидеть за почтовым ящиком, ожидая ответа. Большая часть операции будет осуществляться почтовым отделением; вам не нужно нанимать работника, чтобы ничего не делать, кроме как нянчить почту. То же самое и здесь. Подавляющая часть работы будет осуществляться сетью; почему вы нанимаете нить, чтобы присматривать за ней? Просто найдите асинхронный метод загрузки HTML, который возвращает вам задание и ждет выполнения задачи. HttpClient.GetAsync сразу приходит на ум, хотя могут быть и другие.

Третья проблема: мы создали объект на рабочем потоке. Кто сказал, что безопасно использовать его в потоке пользовательского интерфейса? Существует много «потоковных моделей», которые может иметь объект; в COM-мире они традиционно называются «квартирами» (вы можете говорить со мной только по теме, в которой вы меня создали), «аренда» (вы можете поговорить со мной по любой теме, но вы должны убедиться, что ни одна из двух попыток в то же время), «бесплатно» (все идет, объект в безопасности) и еще несколько других. Предполагается, что объект, о котором идет речь, безопасен для «аренды» или лучше - чтение не будет происходить в потоке пользовательского интерфейса, пока запись не будет выполнена в рабочем потоке. Но если объект действительно является «квартирой», тогда у вас есть объект, с которым вы не можете разговаривать ни на одном потоке, кроме рабочего потока, который вы просто выбросили. Это потенциальный реальный беспорядок.

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

+1

Храните все в одной и той же теме столько, сколько сможете. Я буду иметь это в виду. ОК не нужно возиться с 'ContinueWith'?Я ненавижу следующие устаревшие учебники. но вы также не можете найти обновленные. спасибо за это, я просто следил за учебником, и он упомянул 'ContinueWith', конечно, будучи новым для C#. Я ничего не знаю об этом, поэтому я спросил здесь. , но спасибо за освобождение. Асинхронный и ожидающий – Zion

+0

@Zion: 'await' по сути означает, что« остальная часть этого метода является «ContinueWith» задачи, значение выражения 'await' является результатом». При использовании async/await на C# 5 должно быть много обучающих программ. Хорошее место для запуска можно найти здесь: https://blogs.msdn.microsoft.com/ericlippert/2011/10/03/async-articles/, но там много. –

3

Вам необходимо получить к нему доступ из потока графического интерфейса пользователя. WInforms предоставляет команду invoke для этого ocassion.

listView1.Invoke(() => { 
      foreach(HtmlNode node in nodes) 
      { 
       listView1.Items.Add(node.InnerText); 
      } 
})); 
+0

'listView1.Invoke()'? im не эксперт C#, но 'listView1' - это список, а не' событие'. не 'invoke' используется для' events'? – Zion

+0

Да и нет. Элементы управления WinForms предоставляют этот метод для ввода команд в свой цикл сообщений пользовательского интерфейса. – srandppl

+0

Я получаю то, что вы говорите, но где в коде мы вызываем listView? мы вызываем его в этой строке? 'Task.Factory.StartNew (() => get_url_contents (textBox1.Text)). ContinueWith (t => t.Id, TaskScheduler.FromCurrentSynchronizationContext());' – Zion

Смежные вопросы