К сожалению, принятый ответ, к сожалению, продолжает идти по неправильному пути, в котором вы оказались в первую очередь. Проблема здесь в том, что «мне пришлось обновить пользовательский интерфейс из неправильного потока, выполнив асинхронную операцию над рабочим потоком». Приведенное решение: «иметь рабочий поток, маршал, вызов обратно в поток пользовательского интерфейса». Лучшее решение не в конечном итоге пытается сделать работу с пользовательским интерфейсом на рабочем потоке.
Также нет необходимости гадать с 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
.
Я получил это для работы. сделав еще один метод, который возвращает что-то, а затем запустить его на 'метод ContinueWith' как это: ' ContinueWith (т => other_method_that_processes_results (t.Result), ..........) ' Однако я хотел бы просто сделать все это одним способом. но, видимо, это недопустимо – Zion
Правильное решение было предоставлено слишком много раз на SO. Давайте притворимся, что OP уже искали и не обнаружили, что это не интересно ... Или просто downvote по вопросу ... –