当前位置:  开发笔记 > 编程语言 > 正文

C#任务和void方法

如何解决《C#任务和void方法》经验,为你挑选了1个好方法。

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.

基本上,什么即时试图做的是你键入一个urltextBox1,当你点击button1它会去那个网址,并提取所有的链接的文本.

然而,append这些结果listView1我不断收到此错误

错误信息:

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

我们如何纠正这个?



1> Eric Lippert..:

不幸的是,(先前)接受的答案继续沿着你最终的错误路径继续前进.这里的问题是"我最终需要通过在工作线程上执行异步操作来从错误的线程更新UI".给出的解决方案是"让工作线程将回调封送回UI线程".更好的解决方案是不要首先尝试在工作线程上进行UI工作.

也没有必要捣乱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并返回一个任务.然后我们等待这项任务.等待任务意味着"立即返回我的调用者 - 再次调用按钮点击的消息循环 - 以保持UI运行.当任务完成时,此方法将在此处恢复,并获取任务计算的值. "

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

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

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

现在,这里有一些问题.

如果在我们等待加载HTML时再次单击该按钮会发生什么?我们尝试再次加载它!在等待之前关闭按钮并在之后再次打开是一个好主意.

另外,正如我之前提到的那样,为什么我们要为网络绑定操作产生一个线程呢?你想寄信给你的阿姨并得到答复; 您不必雇佣工人将信件带到邮箱,邮寄,然后坐在邮箱旁等待回复.大部分行动将由邮局完成; 除了照看邮局之外,你不需要雇佣工人什么也不做.这里也是一样的.绝大多数工作将由网络完成; 你为什么要雇一个线来照顾它?只需找到一个异步HTML加载方法,它可以让您返回任务,并等待任务.HttpClient.GetAsync尽管可能还有其他人,但我会立刻想起来.

第三个问题:我们在工作线程上创建了一个对象.谁说在UI线程上使用它是安全的?对象可以有许多"线程模型"; 在COM世界中,这些传统上被称为"公寓"(你只能在我创建的线程上与我交谈),"租赁"(你可以在任何线程上与我交谈,但你需要确保没有两个线程尝试在同一时间),"免费"(任何事情,对象是安全的)和其他一些.这里的假设是所讨论的对象对于"出租"或更好是安全的 - 在工作线程上完成写入之前,UI线程上不会发生读取.但是,如果对象本身就是"公寓",那么你有一个对象,你无法在任何线程上与之交谈,而是你刚刚扔掉的工作线程.这是一个潜在的真正混乱.

这里故事的寓意是,首先,尽可能地将所有内容保存在同一个线程中,然后不要将你的程序内部变为异步工作; 只是用await.

推荐阅读
mobiledu2402851377
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有