【问题标题】:C# External class threading (AutoResetEvent)C# 外部类线程(AutoResetEvent)
【发布时间】:2015-12-15 00:13:58
【问题描述】:

我在处理来自外部类的线程时遇到问题。我在线程方面还很陌生,所以很多事情对我来说仍然是个谜,所以请记住这一点。

我进行了研究,发现了许多关于它的主题,包括:

而且看起来很清楚,但仍然对我没有帮助。这是我的代码:

    public DownloadContent()
    {
        adres = @"...";

        wb = new WebBrowser();
        wb.Navigating += (object sender, WebBrowserNavigatingEventArgs e) => objWait.WaitOne();
        wb.DocumentCompleted += (object sender, WebBrowserDocumentCompletedEventArgs e) => objWait.Set(); //Here is the problem
        wb.DocumentCompleted += OnDocumentCompleted; 
        wb.Navigate(adres);

        MessageBox.Show("after"); //should print after OnDocumentCompleted
    }


    private void OnDocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        //some logic
    }

问题是这个 WebBrowser 类使用单独的线程来导航和完成文档。这没有什么问题,但我不知道我的主线程应该如何与之通信。我试图让原始线程等待,但存在一个问题,即假设再次启动它的函数 objWait.Set() 被当前冻结的主线程调用。我认为这是真正的问题。我尝试了许多奇怪的方法来使它起作用:

  • 为 wb.Navigate(...) 创建另一个线程;它不起作用,因为它不能在单线程上工作;
  • 为 objWait.Set() 单独创建线程;也没用,不知道为什么;
  • 还有一些更奇怪的东西。

我知道这对某些人来说可能微不足道,但我已经坚持了好几个小时了,我真的不知道该怎么办。因此,我将不胜感激。

************************************************编辑*** ************************************************

谢谢大家的回答。我看到很多人注意到我最初的问题并给了我一些建议,我很感激,你让我的工作更轻松。话虽这么说,这个任务的本质是找出是否有任何处理它的好方法。无论如何,感谢您的所有建议和指导,一旦我完成了这个小项目(当时只有一件事),我会仔细研究它们。

我想我可以这样做:

    public DownloadContent()
    {
        ...
        bool flag = true;
        wb.DocumentCompleted += OnDocumentCompleted; 
        wb.DocumentCompleted += (object sender, WebBrowserDocumentCompletedEventArgs e) => flag = false;

        wb.Navigate(adres);

        while(flag);
        MessageBox.Show("after"); //should print after OnDocumentCompleted
    }

但我不知道这是否被认为是有效或优雅的解决方案。如果您对此有任何想法,我将不胜感激。提前谢谢你。

【问题讨论】:

  • 您是要下载网页的 HTML 还是要渲染页面以供打印或类似的东西?
  • 我想从网站获取一些信息。我遵循本指南中的第一种方法:link 我使用 wb 因为我需要访问

    元素。
  • 文章还展示了WebClient和“HtmlAgilityPack”的使用。如果您需要访问 HTML 源代码并提取数据,这就是您要走的路。如果你这样做,代码是单线程的并且非常快。
  • WebBrowser 不能在任何线程上工作,你必须创建一个特殊的。一个线程,它是 STA 并实现了 STA 合约。这需要泵送一个消息循环(Application.Run)并且绝不能阻塞。违反 STA 合同会导致死锁并阻止 WebBrowser 引发任何事件,这是您发现的那种麻烦。一旦你有了一个 STA 线程,那么让它从你的主线程做一些事情就变得非常容易。示例代码is here.

标签: c# multithreading external autoresetevent


【解决方案1】:

这样的事情怎么样:

public DownloadContent()
    {

        AutoResetEvent ase = new AutoResetEvent(false);

        System.Threading.Tasks.Task.Factory.StartNew(()=>
{
        adres = @"...";

        wb = new WebBrowser();

        wb.DocumentCompleted += (object sender, WebBrowserDocumentCompletedEventArgs e) => ase.Set(); //Here is the problem

        wb.Navigate(adres);
});

ase.WaitOne();
        MessageBox.Show("after"); //should print after OnDocumentCompleted
    }

我们在不同的线程中开始下载,并阻塞当前线程直到文档完成。

【讨论】:

  • 我在展示我的方法时似乎遇到了同样的问题。我不知道为什么我收到波兰语的调试消息,但我会尝试翻译它:ThreadStateException...“无法创建 ActiveX 格式的外观(?)'8856f961-340a-11d0-a96b-00c04fd705a2',因为当前线程没有' t 在单线程单元中工作(?)”。我将 (?) 放在我可能翻译错误的单词旁边。
  • 那个错误是说“WebBrowser”需要从某种类型的“线程”创建。线程有不同的风格(例如 STA、MTA)。根据上面的 cmets,我认为你让这变得比你需要的更复杂。试着问一个不同的问题来描述你想要实现的目标。
【解决方案2】:

这不是对您问题的直接回答,但根据您的评论,您所追求的只是在一对 <p> 标记之间提取文本,那么您的工作就很难了。

执行您想要的操作的更快、更简单和单线程的方法是将WebClient 与“HtmlAgilityPack”一起使用。方法如下:

using (var wc = new System.Net.WebClient())
{
    var html = wc.DownloadString(@"http://www.microsoft.com");
    var doc = new HtmlAgilityPack.HtmlDocument();
    doc.LoadHtml(html);
    var node = doc.DocumentNode.SelectSingleNode("/html/body/p");
    Console.WriteLine(node.InnerText);
}

目前产生这个结果:

您当前的 User-Agent 字符串似乎来自自动化进程,如果不正确,请单击此链接:美国英语 Microsoft 主页

【讨论】:

  • 感谢您的回复。的确,它没有回答问题,但给了我一些不错的提示。实际上我想要多个节点,但我想也有一种方法。我最初的方法是使用它,但我遇到了我开始深入研究的这些线程。这纯粹是出于我个人学习目的的学术课程,所以我仍然很好奇如何管理这个外部线程。是的,在这个例子中,我可以使用你刚刚向我展示的内容(对此我很感激),但有一天我可能需要再次处理这样的问题,所以我想知道这个问题是否有解决方案。跨度>
  • @Bielik - 线程的解决方案通常是不使用线程。通常它实际上会使事情变得更慢或过于复杂。更好的选择是使用 TPL 或 Reactive Extensions 等抽象来为您提供多线程,但不会让人头疼。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-20
  • 2021-10-20
相关资源
最近更新 更多