最新消息:20210816 当前crifan.com域名已被污染,为防止失联,请关注(页面右下角的)公众号

【已解决】C#中,将耗时的操作移至独立的线程,以提高UI界面相应速度

C# crifan 6373浏览 0评论

【问题】

一个C#的Winform程序,功能已经实现了。

但是存在一个问题:

对于,程序正在运行中,其内部会涉及到网络的操作,比如下载页面等等,

然后运行中的程序,对于界面的事件,比如点击一个按钮,响应很慢,极其的慢,甚至会丢失,无法响应:

harldy to capture when searching

现在希望:

当程序运行中,内部正在进行访问网络等的耗时操作的时候,对于界面中的按钮点击等事件,也可以顺畅的响应,并可以及时处理。

比如停止正在运行中的搜索。

【解决过程】

1.其实关于这方面的内容,之前就折腾过:

【已解决】给C#程序添加滚动进度条(ProgressBar),实现滚动/动态更新

所以后来是添加了对应的:

1
System.Windows.Forms.Application.DoEvents();

去缓解UI无响应的问题,此时的情况就是,已经加了上述代码了,但是结果还是响应很慢。

2。参考:

WebRequest hangs user interface

知道了,本身Winform的UI线程,就是有个大loop循环,不停更新UI,显示UI。

然后此线程中,理当,不应该去执行,比较耗时的网络处理的,

此点,就像android程序一样,也是类似的逻辑。

所以,需要用到C#中的其他机制,比如

BackgroundWorker

去处理,将耗时的网络处理,放到BackgroundWorker中去。

同时,也去参考:

BackgroundWorker Threads and Supporting Cancel

去写自己的代码。

3.现在就去修改,针对本来很耗时的:

1
string searchResultHtml = crifanLib.getUrlRespHtml(fiverSearchUrl);

操作。

但是,参考过程中,发现,其示例代码,都是把耗时的操作,弄成for循环的,能在每次循环中,去做自己需要做的,耗时的,事情。

然后也会判断,是否被cancel掉了。

然后发现被cancel了,设置cancel,然后另外的bw_RunWorkerCompleted中才有机会去判断e.Cancelled,然后可以及时取消的。

但是,我此处的,耗时的操作,没法被分解为,for循环的那种,所以没有机会去在循环中判断是否被取消,所以无法实现所需要的取消任务的效果。

不过,先不管了,先试试下面的代码:

1
2
3
//string searchResultHtml = crifanLib.getUrlRespHtml(fiverSearchUrl);
getUrlRespHtml_bw(fiverSearchUrl);
string searchResultHtml = curRespHtml;

以及

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
private void getUrlRespHtml_bw(string url)
{
    // Create a background thread
    BackgroundWorker bw = new BackgroundWorker();
    bw.DoWork += new DoWorkEventHandler(bw_DoWork);
    //bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
 
    //init
    curUrl = url;
 
    // run in another thread
    bw.RunWorkerAsync();
}
 
 
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
    curRespHtml = crifanLib.getUrlRespHtml(curUrl);
}
 
//private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
//{
//    // The background process is complete. First we should hide the
//    // modal Progress Form to unlock the UI. Then we need to inspect our
//    // response to see if an error occurred, a cancel was requested or
//    // if we completed successfully.
     
//    // Check to see if an error occurred in the background process.
//    if (e.Error != null)
//    {
//        MessageBox.Show(e.Error.Message);
//        return;
//    }
 
//    // Check to see if the background process was cancelled.
//    if (e.Cancelled)
//    {
//        MessageBox.Show("Processing cancelled.");
//        return;
//    }
 
//    // Everything completed normally.
//    // process the response using e.Result
//    MessageBox.Show("Processing is complete.");
//}

看看效果。

结果貌似是可以执行,但是结果还是UI相应极其的慢。没有达到对应的效果:让UI反应很顺畅。

4.经过折腾,目前如下代码:

调用部分:

1
2
3
4
5
6
7
8
9
//string searchResultHtml = crifanLib.getUrlRespHtml(fiverSearchUrl);
 
string searchResultHtml = "";
getUrlRespHtml_bw(fiverSearchUrl);
while (bWorkNotCompleted)
{
    System.Windows.Forms.Application.DoEvents();
}
searchResultHtml = curRespHtml;

核心部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
private void getUrlRespHtml_bw(string url)
{
    // Create a background thread
    BackgroundWorker m_bgWorker = new BackgroundWorker();
    m_bgWorker.DoWork += new DoWorkEventHandler(m_bgWorker_DoWork);
    m_bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler
                ( m_bgWorker_RunWorkerCompleted );
 
    //init
    bWorkNotCompleted = true;
     
    // run in another thread
    m_bgWorker.RunWorkerAsync(url);
}
 
 
private void m_bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
    string url = (string)e.Argument;
    e.Result = crifanLib.getUrlRespHtml(url);
}
 
void m_bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    bWorkNotCompleted = true;
}
 
private void m_bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // The background process is complete. We need to inspect
    // our response to see if an error occurred, a cancel was
    // requested or if we completed successfully.
 
    // Check to see if an error occurred in the
    // background process.
    if (e.Error != null)
    {
        //MessageBox.Show(e.Error.Message);
        return;
    }
 
    // Check to see if the background process was cancelled.
    if (e.Cancelled)
    {
        //MessageBox.Show("Cancelled ...");
    }
    else
    {
        bWorkNotCompleted = false;
 
        // Everything completed normally.
        // process the response using e.Result
        //MessageBox.Show("Completed...");
        curRespHtml = e.Result.ToString();
    }
}

算是基本工作的,算是,UI响应的速度,有了很大改观。

但是还是不是那种,UI响应,任何时刻,都是十分流畅的。

所以还是没有实现最终目的。

暂时只折腾到这里,有空继续再优化和完善。

5.经过后来的调试,发现,其实是由于自己,另外还有一处,访问网络的代码,没有调用BackgroundWorker,而导致了,之前只是部分提高了UI响应速度。

然后也去把对应的:

1
string gitHtml = crifanLib.getUrlRespHtml(gigUrl);

改为:

1
2
3
4
5
6
7
8
//string gitHtml = crifanLib.getUrlRespHtml(gigUrl);
string gitHtml = "";
getUrlRespHtml_bw(gigUrl);
while (bWorkNotCompleted)
{
    System.Windows.Forms.Application.DoEvents();
}
gitHtml = curRespHtml;

然后,就可以正常实现所需要的:

任何时刻,包括BackgroundWorker在背后去访问网络,UI的响应速度,都和没有执行网络访问,效果是一样的流畅的。

 

【总结】

将所有的,耗时操作,比如我此处,代码中的两处的网络访问的部分的代码,都改用BackgroundWorker去实现,

由此,即可实现:不论程序是否在(利用BackgroundWorker在背后去)执行耗时的操作,整体的UI的响应速度,都十分流畅。

 

具体实现代码:

  • 原先的是:
1
string searchResultHtml = crifanLib.getUrlRespHtml(curSearchInfo.searchUrl);

 

  • 改为通过BackgroundWorker的实现耗时操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/* 1. related global variables */
private bool bWorkNotCompleted = true;
private string curRespHtml = "";
 
 
/* 2. BackgroundWorker implementation */
private void getUrlRespHtml_bw(string url)
{
    // Create a background thread
    BackgroundWorker m_bgWorker = new BackgroundWorker();
    m_bgWorker.DoWork += new DoWorkEventHandler(m_bgWorker_DoWork);
    m_bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler
                ( m_bgWorker_RunWorkerCompleted );
 
    //init
    bWorkNotCompleted = true;
     
    // run in another thread
    m_bgWorker.RunWorkerAsync(url); /* pass parameter */
}
 
private void m_bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
    string url = (string)e.Argument; /* got input parameter */
    /* move time-consuming work into this  xxx_DoWork */
    e.Result = crifanLib.getUrlRespHtml(url);
}
 
void m_bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    bWorkNotCompleted = true;
}
 
private void m_bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // The background process is complete. We need to inspect
    // our response to see if an error occurred, a cancel was
    // requested or if we completed successfully.
 
    // Check to see if an error occurred in the
    // background process.
    if (e.Error != null)
    {
        //MessageBox.Show(e.Error.Message);
        return;
    }
 
    // Check to see if the background process was cancelled.
    if (e.Cancelled)
    {
        //MessageBox.Show("Cancelled ...");
    }
    else
    {
        bWorkNotCompleted = false;
 
        // Everything completed normally.
        // process the response using e.Result
        //MessageBox.Show("Completed...");
        curRespHtml = e.Result.ToString();
    }
}
 
/* call BackgroundWorker version function */
//string searchResultHtml = crifanLib.getUrlRespHtml(curSearchInfo.searchUrl);
getUrlRespHtml_bw(curSearchInfo.searchUrl);
while (bWorkNotCompleted)
{
    /* allow UI update */
    System.Windows.Forms.Application.DoEvents();
}
string searchResultHtml = curRespHtml;

由此,即可实现,将耗时的部分,都移至到了BackgroundWorker里面去,

然后使得UI的响应,就很流程,不会像之前卡顿,无响应了。

转载请注明:在路上 » 【已解决】C#中,将耗时的操作移至独立的线程,以提高UI界面相应速度

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
82 queries in 0.197 seconds, using 22.14MB memory