C# 在C中改进webbrowsers URL导航过程的线程概念#

C# 在C中改进webbrowsers URL导航过程的线程概念#,c#,winforms,webbrowser-control,C#,Winforms,Webbrowser Control,我用C#应用程序开发了一个简单的Id检查windows窗体,通过使用webbrowser控件传递到网页并获得回复来检查一组给定Id是否有效,一切正常,20个Id一个接一个地需要40-60秒。现在我想使用C#中的高级线程概念来加快相同的过程 代码运行良好,我希望使用线程提高性能。今天,任何简单的建议都会大有帮助 private void button2_Click(object sender, EventArgs e) { string url = "https

我用C#应用程序开发了一个简单的Id检查windows窗体,通过使用webbrowser控件传递到网页并获得回复来检查一组给定Id是否有效,一切正常,20个Id一个接一个地需要40-60秒。现在我想使用C#中的高级线程概念来加快相同的过程

代码运行良好,我希望使用线程提高性能。今天,任何简单的建议都会大有帮助

private void button2_Click(object sender, EventArgs e)
       {
           string url = "https://idscheckingsite.com";
           WebBrowser wb = new WebBrowser();
           wb.ScriptErrorsSuppressed = true;
           wb.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(Final_DocumentCompleted);
           wb.Navigate(url);

       }

private void Final_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
       {
           WebBrowser wbs = sender as WebBrowser;
           wbs.Document.GetElementById("pannumber").InnerText = ListsofIds[ids];
           wbs.Document.GetElementById("frmType1").SetAttribute("value", "24Q");
           HtmlElement btnlink = wbs.Document.GetElementById("clickGo1");
           btnlink.InvokeMember("Click");

           //string response = wbs.DocumentText;
           wbs.DocumentCompleted -= new WebBrowserDocumentCompletedEventHandler(Final_DocumentCompleted);
           wbs.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(Final_result);
       }


private void Final_result(object sender, WebBrowserDocumentCompletedEventArgs e)
       {

           WebBrowser wbResult = sender as WebBrowser;

           string status = wbResult.Document.GetElementById("status").InnerText;
           string name = wbResult.Document.GetElementById("name").InnerText;

           wbResult.DocumentCompleted -= new WebBrowserDocumentCompletedEventHandler(Final_result);
           wbResult.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(Final_DocumentCompleted);

           DataRow dr = dt.NewRow();

           dr[0] = PANNumber[ids];
           dr[1] = status;
           dr[2] = name;

           dt.Rows.Add(dr);
           ++ids;

           if (ids < 20)
               wbResult.Navigate(vurl);
           else
           {
               dataGridView1.DataSource = dt;
           }
       }
private void按钮2\u单击(对象发送者,事件参数e)
{
字符串url=”https://idscheckingsite.com";
WebBrowser wb=新的WebBrowser();
wb.ScriptErrorsSuppressed=true;
wb.DocumentCompleted+=新的WebBrowserDocumentCompletedEventHandler(最终文档已完成);
wb.Navigate(url);
}
私有无效最终文档已完成(对象发送者,WebBrowserDocumentCompletedEventArgs e)
{
WebBrowser wbs=发件人作为WebBrowser;
wbs.Document.GetElementById(“pannumber”).InnerText=ListsofIds[ids];
wbs.Document.GetElementById(“frmType1”).SetAttribute(“值”、“24Q”);
HtmlElement btnlink=wbs.Document.GetElementById(“clickGo1”);
调用成员(“单击”);
//字符串响应=wbs.DocumentText;
wbs.DocumentCompleted-=新的WebBrowserDocumentCompletedEventHandler(最终文档已完成);
wbs.DocumentCompleted+=新的WebBrowserDocumentCompletedEventHandler(最终结果);
}
私有无效最终结果(对象发送方,WebBrowserDocumentCompletedEventArgs e)
{
WebBrowser wbResult=发件人作为WebBrowser;
字符串状态=wbResult.Document.GetElementById(“状态”).InnerText;
字符串名称=wbResult.Document.GetElementById(“名称”).InnerText;
wbResult.DocumentCompleted-=新的WebBrowserDocumentCompletedEventHandler(最终结果);
wbResult.DocumentCompleted+=新的WebBrowserDocumentCompletedEventHandler(最终文档已完成);
DataRow dr=dt.NewRow();
dr[0]=PANNumber[id];
dr[1]=状态;
dr[2]=姓名;
dt.Rows.Add(dr);
++身份证;
如果(ids<20)
wbResult.Navigate(vurl);
其他的
{
dataGridView1.DataSource=dt;
}
}

工作正常,但需要使用Advanced C#threading(高级C#线程)概念将性能提高到最大值。

以下是我的建议。单击按钮2时,将启动多个辅助任务。合理的数字是4,但您可以尝试不同的数字,直到获得最佳性能。每个辅助任务将使用自己的
WebBrowser
控件,并将调用ID的子集。例如,工作任务#0将调用ID4、8、12、16和20,工作任务#1将调用1、5、9、13和17等。然后所有工作任务将等待完成,然后可以更新
DataGridView
。不涉及多线程。一切都发生在UI线程中。不需要锁定或其他线程同步

private async void button2_Click(object sender, EventArgs e)
{
    string url = "https://idscheckingsite.com";
    const int WORKER_TASKS_COUNT = 4;
    var workerTasks = new Task[WORKER_TASKS_COUNT];
    for (int i = 0; i < WORKER_TASKS_COUNT; i++)
    {
        workerTasks[i] = DoWorkAsync(i);
    }
    await Task.WhenAll(workerTasks);
    dataGridView1.DataSource = dt;

    async Task DoWorkAsync(int workerIndex)
    {
        using (var wb = new WebBrowser())
        {
            wb.ScriptErrorsSuppressed = true;
            for (int i = 0; i < ListsofIds.Length; i++)
            {
                if (i % WORKER_TASKS_COUNT != workerIndex) continue;
                wb.Navigate(url);
                await wb; // await for the next DocumentCompleted
                wb.Document.GetElementById("pannumber").InnerText = ListsofIds[i];
                wb.Document.GetElementById("frmType1").SetAttribute("value", "24Q");
                HtmlElement btnlink = wb.Document.GetElementById("clickGo1");
                btnlink.InvokeMember("Click");
                await wb; // await for the next DocumentCompleted
                string status = wb.Document.GetElementById("status").InnerText;
                string name = wb.Document.GetElementById("name").InnerText;
                DataRow dr = dt.NewRow();
                dr[0] = PANNumber[i];
                dr[1] = status;
                dr[2] = name;
                dt.Rows.Add(dr);
            }
        }
    }
}

更新:在我自己使用代码之后,我发现
等待wb
有点混乱,因为
WebBrowser
控件有许多可以等待的事件。因此,通过创建事件的异步版本(而不是等待程序),我使其更加明确和可扩展:

然后,创建更多的扩展方法(例如
NavigatedAsync
DocumentTitleChangedAsync
)就变得微不足道了


更新:无休止地等待不是很好,因此可以在等待的扩展方法中添加超时(以毫秒为单位)作为参数。由于整个代码是专门在UI线程中运行的,所以我使用了
System.Windows.Forms.Timer
,尽管
CancellationToken
通常会更方便。为了避免内存泄漏,代码有点复杂,这可能是一个要运行数小时并执行数千个web请求的应用程序的问题

public static class WebBrowserExtensions
{
    public static Task<Uri> DocumentCompletedAsync(this WebBrowser wb, int timeout)
    {
        var tcs = new TaskCompletionSource<Uri>();
        WebBrowserDocumentCompletedEventHandler handler = null;
        var timeoutRegistration = WithTimeout(tcs, timeout,
            () => wb.DocumentCompleted -= handler);
        handler = (_, e) =>
        {
            wb.DocumentCompleted -= handler;
            timeoutRegistration.Unregister();
            tcs.TrySetResult(e.Url);
        };
        wb.DocumentCompleted += handler;
        return tcs.Task;
    }
    public static Task<Uri> DocumentCompletedAsync(this WebBrowser wb)
    {
        return wb.DocumentCompletedAsync(30000); // Default timeout 30 sec
    }

    private static TimeoutRegistration WithTimeout<T>(
        TaskCompletionSource<T> tcs, int timeout, Action eventRemove)
    {
        if (timeout == Timeout.Infinite) return default;
        var timer = new System.Windows.Forms.Timer();
        timer.Tick += (s, e) =>
        {
            timer.Enabled = false;
            timer = null;
            eventRemove();
            eventRemove = null;
            tcs.SetException(new TimeoutException());
            tcs = null;
        };
        timer.Interval = timeout;
        timer.Enabled = true;
        return new TimeoutRegistration(() =>
        {
            if (timer == null) return;
            timer.Enabled = false;
            // Make everything null to avoid memory leaks
            timer = null;
            eventRemove = null;
            tcs = null;
        });
    }

    private struct TimeoutRegistration
    {
        private Action _unregister;
        public TimeoutRegistration(Action unregister)
        {
            _unregister = unregister;
        }
        public void Unregister()
        {
            if (_unregister == null) return;
            _unregister();
            _unregister = null;
        }
    }

}

这是我的建议。单击按钮2时,将启动多个辅助任务。合理的数字是4,但您可以尝试不同的数字,直到获得最佳性能。每个辅助任务将使用自己的
WebBrowser
控件,并将调用ID的子集。例如,工作任务#0将调用ID4、8、12、16和20,工作任务#1将调用1、5、9、13和17等。然后所有工作任务将等待完成,然后可以更新
DataGridView
。不涉及多线程。一切都发生在UI线程中。不需要锁定或其他线程同步

private async void button2_Click(object sender, EventArgs e)
{
    string url = "https://idscheckingsite.com";
    const int WORKER_TASKS_COUNT = 4;
    var workerTasks = new Task[WORKER_TASKS_COUNT];
    for (int i = 0; i < WORKER_TASKS_COUNT; i++)
    {
        workerTasks[i] = DoWorkAsync(i);
    }
    await Task.WhenAll(workerTasks);
    dataGridView1.DataSource = dt;

    async Task DoWorkAsync(int workerIndex)
    {
        using (var wb = new WebBrowser())
        {
            wb.ScriptErrorsSuppressed = true;
            for (int i = 0; i < ListsofIds.Length; i++)
            {
                if (i % WORKER_TASKS_COUNT != workerIndex) continue;
                wb.Navigate(url);
                await wb; // await for the next DocumentCompleted
                wb.Document.GetElementById("pannumber").InnerText = ListsofIds[i];
                wb.Document.GetElementById("frmType1").SetAttribute("value", "24Q");
                HtmlElement btnlink = wb.Document.GetElementById("clickGo1");
                btnlink.InvokeMember("Click");
                await wb; // await for the next DocumentCompleted
                string status = wb.Document.GetElementById("status").InnerText;
                string name = wb.Document.GetElementById("name").InnerText;
                DataRow dr = dt.NewRow();
                dr[0] = PANNumber[i];
                dr[1] = status;
                dr[2] = name;
                dt.Rows.Add(dr);
            }
        }
    }
}

更新:在我自己使用代码之后,我发现
等待wb
有点混乱,因为
WebBrowser
控件有许多可以等待的事件。因此,通过创建事件的异步版本(而不是等待程序),我使其更加明确和可扩展:

然后,创建更多的扩展方法(例如
NavigatedAsync
DocumentTitleChangedAsync
)就变得微不足道了


更新:无休止地等待不是很好,因此可以在等待的扩展方法中添加超时(以毫秒为单位)作为参数。由于整个代码是专门在UI线程中运行的,所以我使用了
System.Windows.Forms.Timer
,尽管
CancellationToken
通常会更方便。为了避免内存泄漏,代码有点复杂,这可能是一个要运行数小时并执行数千个web请求的应用程序的问题

public static class WebBrowserExtensions
{
    public static Task<Uri> DocumentCompletedAsync(this WebBrowser wb, int timeout)
    {
        var tcs = new TaskCompletionSource<Uri>();
        WebBrowserDocumentCompletedEventHandler handler = null;
        var timeoutRegistration = WithTimeout(tcs, timeout,
            () => wb.DocumentCompleted -= handler);
        handler = (_, e) =>
        {
            wb.DocumentCompleted -= handler;
            timeoutRegistration.Unregister();
            tcs.TrySetResult(e.Url);
        };
        wb.DocumentCompleted += handler;
        return tcs.Task;
    }
    public static Task<Uri> DocumentCompletedAsync(this WebBrowser wb)
    {
        return wb.DocumentCompletedAsync(30000); // Default timeout 30 sec
    }

    private static TimeoutRegistration WithTimeout<T>(
        TaskCompletionSource<T> tcs, int timeout, Action eventRemove)
    {
        if (timeout == Timeout.Infinite) return default;
        var timer = new System.Windows.Forms.Timer();
        timer.Tick += (s, e) =>
        {
            timer.Enabled = false;
            timer = null;
            eventRemove();
            eventRemove = null;
            tcs.SetException(new TimeoutException());
            tcs = null;
        };
        timer.Interval = timeout;
        timer.Enabled = true;
        return new TimeoutRegistration(() =>
        {
            if (timer == null) return;
            timer.Enabled = false;
            // Make everything null to avoid memory leaks
            timer = null;
            eventRemove = null;
            tcs = null;
        });
    }

    private struct TimeoutRegistration
    {
        private Action _unregister;
        public TimeoutRegistration(Action unregister)
        {
            _unregister = unregister;
        }
        public void Unregister()
        {
            if (_unregister == null) return;
            _unregister();
            _unregister = null;
        }
    }

}

我的应用程序中可能存在重复的No相同的URL但N个ID,以检查我的相同过程i wan
public static class WebBrowserExtensions
{
    public static Task<Uri> DocumentCompletedAsync(this WebBrowser wb, int timeout)
    {
        var tcs = new TaskCompletionSource<Uri>();
        WebBrowserDocumentCompletedEventHandler handler = null;
        var timeoutRegistration = WithTimeout(tcs, timeout,
            () => wb.DocumentCompleted -= handler);
        handler = (_, e) =>
        {
            wb.DocumentCompleted -= handler;
            timeoutRegistration.Unregister();
            tcs.TrySetResult(e.Url);
        };
        wb.DocumentCompleted += handler;
        return tcs.Task;
    }
    public static Task<Uri> DocumentCompletedAsync(this WebBrowser wb)
    {
        return wb.DocumentCompletedAsync(30000); // Default timeout 30 sec
    }

    private static TimeoutRegistration WithTimeout<T>(
        TaskCompletionSource<T> tcs, int timeout, Action eventRemove)
    {
        if (timeout == Timeout.Infinite) return default;
        var timer = new System.Windows.Forms.Timer();
        timer.Tick += (s, e) =>
        {
            timer.Enabled = false;
            timer = null;
            eventRemove();
            eventRemove = null;
            tcs.SetException(new TimeoutException());
            tcs = null;
        };
        timer.Interval = timeout;
        timer.Enabled = true;
        return new TimeoutRegistration(() =>
        {
            if (timer == null) return;
            timer.Enabled = false;
            // Make everything null to avoid memory leaks
            timer = null;
            eventRemove = null;
            tcs = null;
        });
    }

    private struct TimeoutRegistration
    {
        private Action _unregister;
        public TimeoutRegistration(Action unregister)
        {
            _unregister = unregister;
        }
        public void Unregister()
        {
            if (_unregister == null) return;
            _unregister();
            _unregister = null;
        }
    }

}
Registry.SetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION",
    AppDomain.CurrentDomain.FriendlyName, 11000); // IE11