简体   繁体   English

Gui 反应迟钝

[英]Gui becomes less responsive

First of all thanks for such a great community.首先感谢这么棒的社区。 I've learned a lot from your questions and answers here.我从这里的问题和答案中学到了很多东西。 This is my first question on SO, so please be gentle :)这是我关于 SO 的第一个问题,所以请保持温和:)

Ok, but first things first:好的,但首先要做的是:

-1st code version: -第一个代码版本:

private async void buttonWebScrap_Click(object sender, EventArgs e)
{
    ClickLink("/ptk/sun/core/cookie/CookiesHandler.accept");

    await Task.Delay(750);

    if (_backgroundTaskRunning || !ClickLink("msisdn-change")) return;

    _backgroundTaskRunning = true;
    await LongTaskAsync();
}

private async Task LongTaskAsync()
{
    const string previous = "msisdn-pool-prev";
    const string next = "msisdn-pool-next";

    var tempNumbers = new List<object>();
    
    while (true)
    {
        await Task.Delay(750);

        var document = webBrowser.DocumentText;
        var htmlDoc = new HtmlAgilityPack.HtmlDocument();
        htmlDoc.LoadHtml(document);

        var numbers = htmlDoc.DocumentNode.SelectNodes("//a[starts-with(@id, 'msisdn')]");

        tempNumbers.AddRange(from number in numbers
                             where number.Id != previous && number.Id != next
                             select number.InnerText.RemoveEnters().RemoveSpaces().ReplaceSpecificChars());
        tempNumbers.Add("-------------------------");

        if (tempNumbers.Count >= 24)
        {
            listBoxNumbers.Items.AddRange(tempNumbers.ToArray());
            tempNumbers.Clear();
        }
        
        if (ClickLink(next) == false)
        {
            break;
        }
    }
} 

private bool ClickLink(string linkId)
{
    if (webBrowser.Document != null)
    {
        var elementById = webBrowser.Document.GetElementById(linkId);

        if (elementById != null)
        {
            elementById.InvokeMember("click");
        }
        else
        {
            return false;
        }

        if (webBrowser.Document.Window != null)
        {
            webBrowser.Document.Window.ScrollTo(0, 480);
        }
    }
    else
    {
        return false;
    }

    return true;
}

-2nd code version: -2 代码版本:

private void MainForm_Load(object sender, EventArgs e)
    {
    _webBrowserDocuments = new ConcurrentQueue<string>();
    _uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();

    _progress = new Progress<string>();
    _progress.ProgressChanged += (o, s) => _objects.Add(s);

    _objects = new BindingList<string>();
    listBoxNumbers.DataSource = _objects;
}

private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
    WebBrowserEmulation.Delete();
}

private async void buttonWebScrap_Click(object sender, EventArgs e)
{
    await WebBrowserClickLinkAsync("/ptk/sun/core/cookie/CookiesHandler.accept");

    if (_backgroundTaskRunning || !(await WebBrowserClickLinkAsync("msisdn-change"))) return;

    await Task.Delay(5000);
    var cts = new CancellationTokenSource();

    await WebBrowserDocumentDownloadAsync(cts);
    await DocumentParseAsync(_progress, cts);

    _backgroundTaskRunning = true;
}

private async Task DocumentParseAsync(IProgress<string> progress, CancellationTokenSource cts)
{
    await Task.Factory.StartNew(() =>
        {
            while (true)
            {
                string tempDocument;
                if (_webBrowserDocuments.TryDequeue(out tempDocument))
                {
                    var htmlDoc = new HtmlAgilityPack.HtmlDocument();
                    htmlDoc.LoadHtml(tempDocument);

                    var numbers = htmlDoc.DocumentNode.SelectNodes("//a[starts-with(@id, 'msisdn')]");

                    foreach (var number in numbers.Where(number => number.Id != Previous && number.Id != Next).
                                                   Select(x => x.InnerText.RemoveEnters().RemoveSpaces().ReplaceSpecificChars()))
                    {
                        progress.Report(number);
                    }

                    progress.Report("-------------------------");
                }

                if (cts.IsCancellationRequested)
                {
                    break;
                }
            }
        }, cts.Token);
}

private async Task WebBrowserDocumentDownloadAsync(CancellationTokenSource cts)
{
    await Task.Factory.StartNew(async () =>
        {
            while (true)
            {
                await Task.Delay(1000);

                _webBrowserDocuments.Enqueue(webBrowser.DocumentText);

                if (await WebBrowserClickLinkAsync(Next)) continue;
                cts.Cancel();
                break;
            }
        }, new CancellationToken(), TaskCreationOptions.None, _uiScheduler);
}

private async Task<bool> WebBrowserClickLinkAsync(string linkId)
{
    return await Task.Factory.StartNew(() =>
        {
            if (webBrowser.Document != null)
            {
                var elementById = webBrowser.Document.GetElementById(linkId);

                if (elementById != null)
                {
                    elementById.InvokeMember("click");
                }
                else
                {
                    return false;
                }

                if (webBrowser.Document.Window != null)
                {
                    webBrowser.Document.Window.ScrollTo(0, 480);
                }
            }
            else
            {
                return false;
            }

            return true;
        }, new CancellationToken(), TaskCreationOptions.None, _uiScheduler);
}

On the beggining everything is working just fine, but after webscraping about 500 of numbers, the "GUI" is a little bit sluggish.开始时一切正常,但在抓取了大约 500 个数字后,“GUI”有点迟钝。 I don't know if it is with my "bad" understanding of async/await pattern, or something else.我不知道是因为我对 async/await 模式的“糟糕”理解,还是其他什么原因。 I thought that the second version will be better for this task - but it's still sluggish :/.我认为第二个版本会更好地完成这项任务 - 但它仍然很慢:/。 Can someone help me with this?有人可以帮我弄这个吗?

Why I'm using webbrowser control instead of webclient?为什么我使用 webbrowser 控件而不是 webclient? I know it will be a lot easier, but the site from which I'm webscraping is made with (as I see it) Java (jsessionId) + ajax, and there aren't "proper" links.我知道这会容易得多,但是我从中抓取的网站是用(如我所见)Java (jsessionId) + ajax 制作的,并且没有“正确”的链接。

If you need some more details, just write ;)如果您需要更多详细信息,只需写下 ;)

Thanks in advance.提前致谢。

EDIT:编辑:

  • second version is using Methods which are returning Task (or Task) to simplify the await with current SynchronizationContext from MainForm (only two of those)第二个版本是使用返回任务(或任务)的方法来简化 MainForm 中当前 SynchronizationContext 的等待(只有其中两个)

  • first version was the first approach of using await/async (as you can see the LongTaskAsync() method is async with await Task.Delay())第一个版本是使用 await/async 的第一种方法(如您所见,LongTaskAsync() 方法与 await Task.Delay() 是异步的)

  • this is a finished code (without some thinks like getting SynchronizationContext, setting ListBox.DataSource to BindList, etc.) with only 3 controls on winform - webbrowser, listbox and button ;)这是一个完成的代码(没有一些想法,比如获取 SynchronizationContext,将 ListBox.DataSource 设置为 BindList 等)在 winform 上只有 3 个控件 - 网页浏览器、列表框和按钮;)

The slowdown is likely due to adding your values to your user interface.放缓可能是由于将您的值添加到您的用户界面。

During your looping, you're adding the items to a list box:在循环过程中,您将项目添加到列表框中:

    if (tempNumbers.Count >= 24)
    {
        listBoxNumbers.Items.AddRange(tempNumbers.ToArray());
        tempNumbers.Clear();
    }

As you get more and more results, the list box display will actually become a bottleneck, and cause things to slow down.随着你得到越来越多的结果,列表框显示实际上会成为一个瓶颈,并导致事情变慢。 Since the list box must always be updated on the UI thread, this will cause your UI to get less responsive over time.由于列表框必须始终在 UI 线程上更新,因此随着时间的推移,这将导致您的 UI 响应变慢。

Your second option will likely be even worse, as you add items to a BindingList<T> one item at a time in the second option, and each addition will cause a refresh of the UI.您的第二个选项可能会更糟,因为您在第二个选项中一次向BindingList<T>一个项目,并且每次添加都会导致 UI 刷新。

This could be mitigated by using a ListView with VirtualMode set to true, as this prevents the addition of new items from forcing a refresh on the screen.这可以通过使用将VirtualMode设置为 true 的 ListView 来缓解,因为这可以防止添加新项目强制刷新屏幕。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM