Gui变得反应迟钝

本文关键字:反应迟钝 Gui | 更新日期: 2023-09-27 18:13:25

首先感谢这样一个伟大的社区。我从你们的问答中学到了很多。这是我关于S.O的第一个问题,所以请温柔一点:)

好的,但重要的事情先做:

-第一个代码版本:

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;
}

-第二代码版本:

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);
}

一开始一切都很好,但在抓取了大约500个数字后,"用户界面"出现了。有点迟钝。我不知道是不是因为我的"坏"理解async/await模式,或者其他的东西。我认为第二个版本将更好地完成这项任务-但它仍然迟缓:/。有人能帮我一下吗?

为什么我使用浏览器控件而不是web客户端?我知道这将是容易得多,但我从网站抓取是由(我看到它)Java (jsessionId) + ajax,并没有"属性"链接。

如果你需要更多的细节,就写;)

提前感谢。

编辑:

  • 第二个版本使用返回Task(或Task)的方法来简化来自MainForm的当前SynchronizationContext的等待(只有其中两个)

  • 第一个版本是使用await/async的第一个方法(你可以看到LongTaskAsync()方法与await Task.Delay()是async的)

  • 这是一个完成的代码(没有一些想法,如获得SynchronizationContext,设置ListBox。数据源到BindList等),在winform上只有3个控件- web浏览器,列表框和按钮;)

Gui变得反应迟钝

速度变慢可能是由于将值添加到用户界面。

在循环过程中,您将项目添加到列表框中:

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

当您得到越来越多的结果时,列表框显示实际上会成为一个瓶颈,并导致事情变慢。由于列表框必须始终在UI线程上更新,这将导致您的UI随着时间的推移响应速度变慢。

您的第二个选项可能更糟,因为您在第二个选项中每次只向BindingList<T>添加一个项目,并且每次添加都会导致UI刷新。

这可以通过使用VirtualMode设置为true的ListView来缓解,因为这可以防止添加新项目强制刷新屏幕。