C#winform中UI線程与工作線程的交互
1.使用多線程
void CalcPi(int digits)
{
//...這里可寫任意代碼,但如果与UI界面交互將出錯.
}
void calcButton_Click(object sender,EventArgs e)
{
//...這里還是在UI線程里,可以寫任意代碼
//啟動一個新線程,用于處理費時的任務
Thread piThread=new Thread(CalcPiThreadStart);
piThread.Start((int)this.decimalPlacesNumericUpDown.Value);
}
void CalcPiThreadStart(object digits)
{
//線程只能傳object類型的參數.
CalcPi((int)digits);
}
2.在UI線程中調用其它線程
使用了多線程,卻還有兩個問題(1)線程只能傳object類型參數(2)在新的線程中,不能直接修改窗体中的內容.事實上,在与UI交互時,我們一般并不用多線程,而是使用自定義委托.盡管這也是多線程的原理.但操作起來方便的多.
--1.同步調用
void CalcPi(int digits)
{
//...這里如果有与UI界面交互的代碼,則必須要使用回調方法
}
delegate void CalcPiDelegate(int digits);
void calcButton_Click(object sender,EventArgs e)
{
//...
//
CalcPiDelegate calcPi=new CalcPiDelegate(CalcPi);
//同步調用,与calcPi.Invoke作用相同
calcPi((int)this.decimalPlacesNumericUpDown.Value);
}
--2.异步調用
void CalcPi(int digits)
{
//...這里如果有与UI界面交互的代碼,則必須要使用回調方法
}
delegate void CalcPiDelegate(int digits);
void calcButton_Click(object sender,EventArgs e)
{
//...
//
CalcPiDelegate calcPi=new CalcPiDelegate(CalcPi);
//异步調用
calcPi.BeginInvoke(
(int)this.decimalPlacesNumericUpDown.Value,//將此參數傳給CalcPi方法
EndCalcPi,//當CalcPi結束時調用該方法
calcPi);//此參數傳給EndCalcPi方法,好像一定要与委托名稱相同
}
void EndCalcPi(IAsyncResult result)
{
//獲得結果,處理异常和清除資源
try
{
CalcPiDelegate calcPi=(CalcPiDelegate)result.AsyncState;
calcPi.EndInvoke(result);//調用EndInvoke
}
catch(Exception ex)
{
//EndCalcPi在工作者線程中執行
//...這里不能寫与UI交互的代碼,否則出錯
}
}
3.在工作者線程中回調UI線程
不管是同步調用還是异步調用,如果在工作線程中需要顯示消息到UI上,如在工作線程中發生异常或需要報知進度等,這時我們還需要回調UI線程.回調也分同步回調与异步回調.
--1.同步回調
將CalcPi方法改為如下:
void ShowProgress(string pi,int totalDigits,int digitsSoFar)
{
//确保當前處于UI線程
Debug.Assert(this.InvokeRequired==false);
//...這里一般寫与UI線程交互的代碼.以及一些簡短的數据處理.
}
delegate void ShowProgressDelegate(string pi,int totalDigits,int digitsSoFar);
void CalcPi(int digits)
{
StringBuilder pi=new StringBuilder("3",digits+2);
//准備顯示進度
ShowProgressDelegate showProgress=new ShowProgressDelegate(ShowProgress);
//當需要回顯消息時,就調用Control.Invoke
//顯示初始進度,工作線程暫停,等顯示完畢后,再繼續工作線程.
this.Invoke(showProgress,new object[]{pi.ToString(),digits,0});
if(digits>0)
{
pi.Append(".");
for(int i=0;i<digits;i+=9)
{
//...對數据的處理,這是一段耗時的代碼
//顯示變化的進度,工作線程暫停,等顯示完畢后,再繼續工作線程.
this.Invoke(showProgress,new object[]{pi.ToString(),digits,i+digitCount});
}
}
}
--2.异步回調
將CalcPi方法改為如下,其實這段代碼与同步回調的唯一區別就是用BeginInvoke代替了Invoke.异步回調与异步調用有一點不同,那就是,异步回調在Control.BeginInvoke之后不用調用Control.EndInvoke,這是完全安全的.
void ShowProgress(string pi,int totalDigits,int digitsSoFar)
{
//确保當前處于UI線程
Debug.Assert(this.InvokeRequired==false);
//...這里一般寫与UI線程交互的代碼.以及一些簡短的數据處理.
}
delegate void ShowProgressDelegate(string pi,int totalDigits,int digitsSoFar);
void CalcPi(int digits)
{
StringBuilder pi=new StringBuilder("3",digits+2);
//准備顯示進度
ShowProgressDelegate showProgress=new ShowProgressDelegate(ShowProgress);
//當需要回顯消息時,就調用
//通知UI線程去顯示初始進度,并立即返回工作線程.
this.BeginInvoke(showProgress,new object[]{pi.ToString(),digits,0});
if(digits>0)
{
pi.Append(".");
for(int i=0;i<digits;i+=9)
{
//...對數据的處理,這是一段耗時的代碼
//通知UI線程去顯示變化的進度,并立即返回工作線程.
this.BeginInvoke(showProgress,new object[]{pi.ToString(),digits,i+digitCount});
}
}
}
4.總結
--1.同步調用与同步回調結合.
delegate void CalcPiDelegate(int digits);
void calcButton_Click(object sender,EventArgs e)
{
//...
//
CalcPiDelegate calcPi=new CalcPiDelegate(CalcPi);
//同步調用,与calcPi.Invoke作用相同
calcPi((int)this.decimalPlacesNumericUpDown.Value);
}
void ShowProgress(string pi,int totalDigits,int digitsSoFar)
{
//确保當前處于UI線程
Debug.Assert(this.InvokeRequired==false);
//...這里一般寫与UI線程交互的代碼.以及一些簡短的數据處理.
}
delegate void ShowProgressDelegate(string pi,int totalDigits,int digitsSoFar);
void CalcPi(int digits)
{
StringBuilder pi=new StringBuilder("3",digits+2);
//准備顯示進度
ShowProgressDelegate showProgress=new ShowProgressDelegate(ShowProgress);
//同步回調
this.Invoke(showProgress,new object[]{pi.ToString(),digits,0});
if(digits>0)
{
pi.Append(".");
for(int i=0;i<digits;i+=9)
{
//...對數据的處理,這里是一段耗時的代碼
//同步回調
this.Invoke(showProgress,new object[]{pi.ToString(),digits,i+digitCount});
}
}
}
--2.异步調用与异步回調結合.
delegate void CalcPiDelegate(int digits);
void calcButton_Click(object sender,EventArgs e)
{
//...
//
CalcPiDelegate calcPi=new CalcPiDelegate(CalcPi);
//异步調用
calcPi.BeginInvoke(
(int)this.decimalPlacesNumericUpDown.Value,//將此參數傳給CalcPi方法
EndCalcPi,//當CalcPi結束時調用該方法
calcPi);//此參數傳給EndCalcPi方法,好像一定要与委托名稱相同
}
void EndCalcPi(IAsyncResult result)
{
//獲得結果,處理异常和清除資源
try
{
CalcPiDelegate calcPi=(CalcPiDelegate)result.AsyncState;
calcPi.EndInvoke(result);//調用EndInvoke
}
catch(Exception ex)
{
//EndCalcPi在工作者線程中執行
//...如果需要回調UI線程,可使用同步回調或异步回調.
}
}
void ShowProgress(string pi,int totalDigits,int digitsSoFar)
{
//确保當前處于UI線程
Debug.Assert(this.InvokeRequired==false);
//...這里一般寫与UI線程交互的代碼.以及一些簡短的數据處理.
}
delegate void ShowProgressDelegate(string pi,int totalDigits,int digitsSoFar);
void CalcPi(int digits)
{
StringBuilder pi=new StringBuilder("3",digits+2);
//准備顯示進度
ShowProgressDelegate showProgress=new ShowProgressDelegate(ShowProgress);
//當需要回顯消息時,就調用
//通知UI線程去顯示初始進度,并立即返回工作線程.
this.BeginInvoke(showProgress,new object[]{pi.ToString(),digits,0});
if(digits>0)
{
pi.Append(".");
for(int i=0;i<digits;i+=9)
{
//...對數据的處理,這是一段耗時的代碼
//通知UI線程去顯示進度,并立即返回工作線程.
this.BeginInvoke(showProgress,new object[]{pi.ToString(),digits,i+digitCount});
}
}
}
3.同步調用与异步回調結合
delegate void CalcPiDelegate(int digits);
void calcButton_Click(object sender,EventArgs e)
{
//...
//
CalcPiDelegate calcPi=new CalcPiDelegate(CalcPi);
//同步調用,与calcPi.Invoke作用相同
calcPi((int)this.decimalPlacesNumericUpDown.Value);
}
void ShowProgress(string pi,int totalDigits,int digitsSoFar)
{
//确保當前處于UI線程
Debug.Assert(this.InvokeRequired==false);
//...這里一般寫与UI線程交互的代碼.以及一些簡短的數据處理.
}
delegate void ShowProgressDelegate(string pi,int totalDigits,int digitsSoFar);
void CalcPi(int digits)
{
StringBuilder pi=new StringBuilder("3",digits+2);
//准備顯示進度
ShowProgressDelegate showProgress=new ShowProgressDelegate(ShowProgress);
//當需要回顯消息時,就調用
//通知UI線程去顯示初始進度,并立即返回工作線程.
this.BeginInvoke(showProgress,new object[]{pi.ToString(),digits,0});
if(digits>0)
{
pi.Append(".");
for(int i=0;i<digits;i+=9)
{
//...對數据的處理,這是一段耗時的代碼
//通知UI線程去顯示進度,并立即返回工作線程.
this.BeginInvoke(showProgress,new object[]{pi.ToString(),digits,i+digitCount});
}
}
}
--4.异步調用与同步回調結合
delegate void CalcPiDelegate(int digits);
void calcButton_Click(object sender,EventArgs e)
{
//...
//
CalcPiDelegate calcPi=new CalcPiDelegate(CalcPi);
//异步調用
calcPi.BeginInvoke(
(int)this.decimalPlacesNumericUpDown.Value,//將此參數傳給CalcPi方法
EndCalcPi,//當CalcPi結束時調用該方法
calcPi);//此參數傳給EndCalcPi方法,好像一定要与委托名稱相同
}
void EndCalcPi(IAsyncResult result)
{
//獲得結果,處理异常和清除資源
try
{
CalcPiDelegate calcPi=(CalcPiDelegate)result.AsyncState;
calcPi.EndInvoke(result);//調用EndInvoke
}
catch(Exception ex)
{
//EndCalcPi在工作者線程中執行
//...如果需要回調UI線程,可使用同步回調或异步回調.
}
}
void ShowProgress(string pi,int totalDigits,int digitsSoFar)
{
//确保當前處于UI線程
Debug.Assert(this.InvokeRequired==false);
//...這里一般寫与UI線程交互的代碼.以及一些簡短的數据處理.
}
delegate void ShowProgressDelegate(string pi,int totalDigits,int digitsSoFar);
void CalcPi(int digits)
{
StringBuilder pi=new StringBuilder("3",digits+2);
//准備顯示進度
ShowProgressDelegate showProgress=new ShowProgressDelegate(ShowProgress);
//同步回調
this.Invoke(showProgress,new object[]{pi.ToString(),digits,0});
if(digits>0)
{
pi.Append(".");
for(int i=0;i<digits;i+=9)
{
//...對數据的處理,這是一段耗時的代碼
//同步回調
this.Invoke(showProgress,new object[]{pi.ToString(),digits,i+digitCount});
}
}
}