博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用Invoke,BeginInvoke 在多线程中更新UI主线程的元素
阅读量:4042 次
发布时间:2019-05-24

本文共 3680 字,大约阅读时间需要 12 分钟。

在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件不被允许(CLR出於安全考慮,默認是不允許),使用Invoke 和 BeginInvoke可以解决这个问题。

 //先看个例子:通过Task从webservice获取数据后,在页面的Datagridview显示出来.

        public  async Task<DataTable> GetDataAsync(string dateFrom,string dateTo,string cateGory)
        {           
           Task <DataTable > t =   Task.Run(() =>
           {
               DataTable dtRt = new DataTable();
               //1. 准备连接,各种参数             

               string strURL = @"https://*************";// Webservice 的地址

               //创建一个HTTP请求
               HttpWebRequest request = (HttpWebRequest)WebRequest.Create(strURL);
               //Post请求方式
               request.Method = "POST";
               //内容类型
               request.ContentType = "application/json;charset=UTF-8";
               request.Headers.Add("Authorization", DeveloperId);

               //2. 连接 Web service 

               using (var postStream = new StreamWriter(request.GetRequestStream()))
               {
                   try
                   {
                       postStream.Write(jsonParas);
                       postStream.Flush();
                   }
                   catch (Exception e)
                   {
                       MessageBox.Show(e.Message);//连接服务器失败
                                                  //insert a log
                       sqlhelper.InsertLog("*********原因*****");
                       return dtRt;
                   }
               }

               //3.从 webservice 获取返回的数据

               String strValue = "";//strValue为http响应所返回的字符流
               HttpWebResponse response;
               try
               {
                   //获得响应流
                   response = (HttpWebResponse)request.GetResponse();
               }
               catch (WebException ex)
               {
                   response = ex.Response as HttpWebResponse;
               }

               Stream s = response.GetResponseStream();

               StreamReader myStreamReader = new StreamReader(s, Encoding.GetEncoding("utf-8"));
               strValue = myStreamReader.ReadToEnd();
               myStreamReader.Close();
               s.Close();               
                dtRt = JsonHelper.jsonToDataTable(strValue);// 自定义函数:將json轉為datatable

//***************使用 BeginInvoke****************

           dgv1.BeginInvoke(new Action(()=> {
                   dgv1.DataSource = dtRt;
               }));

             // 或: dgv1.BeginInvoke((MethodInvoker)delegate

             // {
             //     dgv1.DataSource = dtRt;
             // });             
               return dtRt;
           });
           return  await t;
        }

另一個進度條的例子:

  private  void button12_Click(object sender, EventArgs e)

        {      
            Task t = Task.Run(() =>
            {
                for (int i = 0; i <= 100; i++)
                {
                    progressBar1.Invoke(new Action(() =>  {
                        progressBar1.Value = i;
                    }));                    
                    Thread.Sleep(100);                    
                }
            
            });          
        }

进一步,再了解下 

 首先invoke和begininvoke的使用有两种情况:

  第一种: control的invoke、begininvoke。

  第二种:delegrate的invoke、begininvoke。  

先学习第一种:control的invoke、begininvoke

1.Control.Invoke 方法 (Delegate) :在拥有此控件的基础窗口句柄的线程上执行指定的委托。

2.Control.BeginInvoke 方法 (Delegate) :在创建控件的基础句柄所在线程上异步执行指定委托。

3.大致理解invoke表是同步 表示异步,它是相对于调用线程的异步,而不是相对于UI线程的异步。两者的区别就是一个导致工作线程等待,而另外一个则不会。(這點文章最后的例子有說明)

4.Control的Invoke和BeginInvoke与Delegate的Invoke和BeginInvoke是不同的。

5.Control的Invoke和BeginInvoke的参数为delegate,delegate中的方法是在Control的线程中执行的,即UI线程中執行。(這點文章最后的例子有說明)

 

因为界面更新始终要通过 UI 线程,我们可以在分线程中进行大部分的逻辑运算,而将界面更新放到 UI 线程中去做,达到了减轻 UI 线程负担的目的。

再举个简单例子,比如启动一个线程,在线程的方法中更新窗体中的一个控件 

//启动一个线程 
Thread thread=new Thread(new ThreadStart(DoWork)); 
thread.Start(); 
//线程方法 
private void DoWork() 
  this.TextBox1.Text="文本框"; 
上面操作,在VS里有异常的
可以使用Invoke\BeginInvoke

如果你的后台线程在更新一个UI控件的状态后不需要等待,而是要继续往下处理,就使用BeginInvoke异步处理。

如果你的后台线程需要操作UI控件,并且需要等到该操作执行完毕才能继续执行,就使用Invoke。

public void DoWork(object txt) --如果是thread調用,注意参数是object。task 調用的話就無謂。

{

Console.WriteLine(" Task thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString()); //Task thread id is: 4 ,,,新的線程

  TextBox1.BeginInvoke(new Action(()=> {

            Console.WriteLine(" Invoke thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString()); //Invoke thread id is:1  ,,,回到UI主線程

                 TextBox1.Text=Convert.Tostring(txt); 

                    Console.WriteLine("Invoke1!");

                    Thread.Sleep(5000);--模擬阻塞

                     Console.WriteLine("Invoke2!");

               }));

      Console.WriteLine("Done!"); // 上面語句如果是BeginInvoke,則這個及后面語句不會被阻塞,它的輸出在“Invoke1”,“Invoke2”之前。反之,用Invoke則會,它的輸出在之後。

}
private void button1_Click(object sender, EventArgs e)
{

 Console.WriteLine("Main Thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString()); //Main Thread id is:1  ,,,,UI 主線程

           Task task = Task.Run(() => DoWork(“Text changed!”));

   //Thread thread = new Thread(DoWork);
   //thread.Start("Text changed!");
}
}
}

转载地址:http://oimdi.baihongyu.com/

你可能感兴趣的文章
本地服务方式搭建etcd集群
查看>>
安装k8s Master高可用集群
查看>>
忽略图片透明区域的事件(Flex)
查看>>
忽略图片透明区域的事件(Flex)
查看>>
AS3 Flex基础知识100条
查看>>
Flex动态获取flash资源库文件
查看>>
flex4 中创建自定义弹出窗口
查看>>
01Java基础语法-15.for循环结构
查看>>
01Java基础语法-16. while循环结构
查看>>
01Java基础语法-18. 各种循环语句的区别和应用场景
查看>>
01Java基础语法-19. 循环跳转控制语句
查看>>
Django框架全面讲解 -- Form
查看>>
socket,accept函数解析
查看>>
今日互联网关注(写在清明节后):每天都有值得关注的大变化
查看>>
”舍得“大法:把自己的优点当缺点倒出去
查看>>
[今日关注]鼓吹“互联网泡沫,到底为了什么”
查看>>
[互联网学习]如何提高网站的GooglePR值
查看>>
[关注大学生]求职不可不知——怎样的大学生不受欢迎
查看>>
[关注大学生]读“贫困大学生的自白”
查看>>
[互联网关注]李开复教大学生回答如何学好编程
查看>>