C#任务和void方法 [英] C# tasks and void methods

查看:104
本文介绍了C#任务和void方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

代码

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)
        {

        }
    }
}

我正在使用Windows窗体并且正在练习C#,对这种语言来说我还很新,但是对Python有一点了解.

Im using windows forms and am practicing C#, Im fairly new to this language but know a little python.

基本上,我想做的是在textBox1上键入url,然后单击button1,它将转到该URL并提取所有链接的文本.

basically, what im trying to do is you type a url on textBox1 and when you click button1 it will go to that url and extract all the link's text.

append将这些结果发送到listView1,但是我不断收到此错误

and append those results to listView1 however I keep getting this error

错误消息:

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

我们如何纠正这个问题?

how do we correct this?

推荐答案

(先前)接受的答案很不幸地继续沿您最初遇到的错误路径前进.这里的问题是我最终需要通过对工作线程执行异步操作来从错误的线程更新UI".给出的解决方案是使工作线程将对回调的调用编组到UI线程".更好的解决方案是不要首先尝试在工作线程上执行UI工作.

The (previously) accepted answer unfortunately continues down the wrong path you ended up on in the first place. The problem here is "I ended up needing to update the UI from the wrong thread by doing an asynchronous operation on a worker thread". The solution given is "have the worker thread marshal the call back to the UI thread". The better solution is don't end up trying to do UI work on a worker thread in the first place.

也没有必要乱搞ContinueWith;在C#5和更高版本中,我们有异步等待.

There is also no need to muck around with ContinueWith; in C# 5 and above we have asynchronous waiting.

让我们假设我们确实有想要在另一个线程上执行的工作. (这是可疑的;这里的高延迟操作没有理由需要在另一个线程上进行.它不受处理器的限制!但是出于争论的缘故,让我们假设我们希望将HTML下载到另一个线程上:

Let's suppose that we genuinely do have work we want to perform on another thread. (This is suspicious; there is no reason why the high-latency operation here needs to go on another thread. It's not processor bound! But for the sake of argument, let's suppose we wish to download the HTML on another thread:

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

请注意,我已经标记了方法async.这不会使其在另一个线程上运行.这意味着在该方法的工作完成之前,该方法将返回给它的调用者(即调度事件的消息循环).它将在将来的某个时刻恢复到该方法内部."

Note that I have marked the method async. This does not make it run on another thread. This means "this method is going to return to its caller -- the message loop that dispatched the event -- before the work of the method is done. It will resume inside the method at some point in the future."

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

我们在这里有什么?我们产生了一个异步任务,该任务加载了一些HTML并返回了一个任务.然后,我们等待该任务.等待任务意味着立即返回给我的调用者-再次单击调度按钮的消息循环-使UI保持运行.任务完成后,此方法将在此处恢复并获取任务计算出的值. "

What do we have here? We spawn off an asynchronous task that loads some HTML and returns a task. We then await that task. Awaiting the task means "return immediately to my caller -- again, the message loop that dispatched the button click -- to keep the UI running. When the task completes, this method will resume here, and obtain the value computed by the task."

现在程序的其余部分完全正常了:

Now the rest of the program is perfectly normal:

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

我们仍然在UI线程上;我们在一个按钮单击处理程序中.在另一个线程上完成的唯一工作就是获取HTML,并且当它可用时,我们就在这里继续.

We're still on the UI thread; we're in a button click handler. The only work that was done on another thread was getting the HTML, and when it was available, we resumed right here.

现在,这里存在一些问题.

Now, there are a few problems here.

如果我们在等待HTML加载时再次单击该按钮会发生什么?我们尝试再次加载!最好在等待之前关闭按钮,然后再重新打开.

What happens if the button is clicked again while we're waiting for the HTML to load? We try to load it again! It would be a good idea to turn the button off before the await and back on again after.

而且,正如我之前提到的,为什么我们产生一个用于网络绑定操作的线程?您想寄一封信给您的姨妈并得到回复;您不必雇用工人将信带到邮箱,将其邮寄,然后坐在邮箱旁等待答复.绝大多数操作将由邮局完成;您无需雇用工人就可以照看邮局.这里也是一样.绝大多数工作将由网络完成;你为什么要雇用一个保姆呢?只需找到一个异步HTML加载方法即可为您提供任务,然后等待任务. HttpClient.GetAsync马上浮现在脑海,尽管可能还有其他人.

Also, as I alluded before, why are we spawning off a thread for a network-bound operation? You want to mail a letter to your aunt and get a reply; you don't have to hire a worker to take the letter to the mail box, mail it, and then sit by the mail box waiting for the reply. The vast bulk of the operation is going to be done by the post office; you don't need to hire a worker to do nothing but babysit the post office. Same thing here. The vast majority of the work will be done by the network; why are you hiring a thread to babysit it? Just find an asynchronous HTML loading method that gives you back a task, and await the task. HttpClient.GetAsync comes immediately to mind, though there may be others.

第三个问题:我们在工作线程上创建了一个对象. 谁说在UI线程上使用它是安全的?一个对象可以拥有许多线程模型";这些模型可以用来创建对象.在COM世界中,这些传统上称为公寓"(您只能在创建我的线程上与我交谈),租赁"(您可以在任何线程上与我交谈,但必须确保没有两个线程尝试)同时),免费"(无论如何,该对象都是安全的)和其他一些.这里的假设是,所讨论的对象对于出租"或更好的对象是安全的-直到在工作线程上完成写操作,才会在UI线程上进行读取.但是,如果对象本身实际上是公寓",那么您有一个对象,您只能在被丢弃的工作线程上与之交谈,而不能在任何线程上与之交谈.这是一个潜在的真正的混乱.

A third problem: we have created an object on the worker thread. Who says that it is safe to use it on the UI thread? There are many "threading models" that an object can have; in the COM world these are traditionally called "apartment" (you can only talk to me on the thread you created me on), "rental" (you can talk to me on any thread but you are required to ensure that no two threads try at the same time), "free" (anything goes, the object is safe) and a few others. The assumption here is that the object in question is safe for "rental" or better -- the read will not happen on the UI thread until the write is done on the worker thread. But if the object is really itself "apartment" then you have an object you can't talk to on any thread but the worker thread you just threw away. This is a potential real mess.

这里故事的寓意是,首先,将所有内容尽可能地保持在同一线程上,其次,不要将程序内翻以使异步工作正常进行;只需使用await .

The moral of the story here is, first, keep everything on the same thread as much as you possibly can, and second don't turn your program inside out to make asynchrony work; just use await.

这篇关于C#任务和void方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆