本文共 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/