其他組件的BeginXXX和EndXXX方法
在其他的.net組件中也有類似BeginInvoke和EndInvoke的方法,如System.Net.HttpWebRequest類的BeginGetResponse和EndGetResponse方法。其使用方法類似於委托類型的BeginInvoke和EndInvoke方法,例如:
class Program
{
//回調函數
private static void requestCompleted(IAsyncResult asyncResult)
{
if (asyncResult == null || asyncResult.AsyncState==null)
{
Console.WriteLine("回調失敗");
return;
}
HttpWebRequest hwr = asyncResult.AsyncState as HttpWebRequest;
HttpWebResponse response = (HttpWebResponse)hwr.EndGetResponse(asyncResult);
StreamReader sr = new StreamReader(response.GetResponseStream());
string str = sr.ReadToEnd();
Console.WriteLine("返回流長度:"+str.Length);
}
static void Main(string[] args)
{
HttpWebRequest request =
(HttpWebRequest)WebRequest.Create("http://www.baidu.com");
//異步請求
IAsyncResult asyncResult = request.BeginGetResponse(requestCompleted, request);
Console.WriteLine("任務開始");
Console.Read();
}
}
由委托啟動的線程的狀態控制
委托的EndInvoke方法阻止當前線程運行,直到委托異步執行完成。
IAsyncResult.IsCompleted屬性表示委托異步執行是否完成。
委托的WaitOne方法等待異步方法執行完成。
使用BackgroundWorker組件
主要的事件及參數:
1.DoWork—當執行BackgroundWorker.RunWorkerAsync方法時會觸發該事件,並且傳遞DoWorkEventArgs參數;
2.ProgressChanged—操作處理中獲得的處理狀態變化,通過BackgroundWorker.ReportProgress方法觸發該事件,並且傳遞ProgressChangedEventArgs,其中包含了處理的百分比,這個參數在UI界面上設置progressbar控件。
3.RunWorkerCompleted—異步操作完成或中途終止會觸發該事件。如果需要提前終止執行後台操作,可以調用BackgroundWorker.CancelAsync方法。在處理DoWork事件的函數中檢測BackgroundWorker.CancellationPending屬性是否為true,如果是true,則表示用戶已經取消了異步調用,同時將DoWorkEventArgs.Cancel屬性設為true(傳遞給處理DoWork事件的函數的第二個參數),這樣當退出異步調用的時候,可以讓處理RunWorkerCompleted事件的函數知道是正常退出還是中途退出。
主要的方法:
1. BackgroundWorker.RunWorkerAsync—"起動"異步調用的方法有兩次重載RunWorkerAsync(),RunWorkerAsync(object argument),第二個重載提供了一個參數,可以供異步調用使用。(如果有多個參數要傳遞怎麼辦,使用一個類來傳遞他們吧)。調用該方法後會觸發DoWork事件,並且為處理DoWork事件的函數傳遞DoWorkEventArg參數,其中包含了RunWorkerAsync傳遞的參數。在相應DoWork的處理函數中就可以做具體的復雜操作。
2. BackgroundWorker.ReportProgress—需要在一個冗長的操作中向用戶不斷反饋進度,這樣的話就可以調用的ReportProgress(int percent),在調用 ReportProgress 方法時,觸發ProgressChanged事件。提供一個在 0 到 100 之間的整數,它表示後台活動已完成的百分比。你也可以提供任何對象作為第二個參數,允許你給事件處理程序傳遞狀態信息。作為傳遞到此過程的 ProgressChangedEventArgs 參數屬性,百分比和你自己的對象(如果提供的話)均要被傳遞到 ProgressChanged 事件處理程序。這些屬性被分別命名為 ProgressPercentage 和 UserState,並且你的事件處理程序可以以任何需要的方式使用它們。(注意:只有在BackgroundWorker.WorkerReportsProgress屬性被設置為true該方法才可用)。
3. BackgroundWorker.CancelAsync—但需要退出異步調用的時候,就調用的這個方法。但是樣還不夠,因為它僅僅是將BackgroudWorker.CancellationPending屬性設置為true。你需要在具體的異步調用處理的時候,不斷檢查BackgroudWorker.CancellationPending是否為true,如果是真的話就退出。(注意:只有在BackgroundWorker.WorkerSupportsCancellation屬性被設置為true該方法才可用)。
//按鈕事件
private void button1_Click(object sender, EventArgs e)
{
this.backgroundWorker1.RunWorkerAsync();//處理事務
}
//處理事務事件
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//初始化進度條
this.progressBar1.Maximum = 100;
this.progressBar1.Minimum = 0;
//模擬事物處理
for (int i = 0; i < 100; i++)
{
Thread.Sleep(10);
//局部操作完成事件觸發
this.backgroundWorker1.ReportProgress(i, null);
}
}
//局部操作完成時執行的方法
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;//設置進度條值
}
//事物處理完成時觸發
private void backgroundWorker1_RunWorkerCompleted(object sender,RunWorkerCompletedEventArgs e)
{
MessageBox.Show(null, "工作線程完成!", "提示");
}
線程池的使用
class Program
{
//線程方法
public static void ThreadProc(object i)
{
Console.WriteLine(i.ToString());
Thread.Sleep(1000);
}
public static void Main()
{
ThreadPool.SetMaxThreads(3, 3);//設置線程池
for (int i = 0; i < 10; i++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc), "線程" + i);
}
Console.WriteLine("運行結束");
Console.Read();
}
}
每一個進程都有一個線程池,線程池的默認大小是25,我們可以通過SetMaxThreads方法來設置其最大值。
注意:因為WaitCallback委托的原型是void WaitCallback(object state),那沒有辦法,我們只能將多個參數封裝到一個Object中。
使用Semaphore
Semaphore類:限制可同時訪問某一資源或資源池的線程數。
class Program
{
private static Semaphore semaphore = new Semaphore(0, 5);//初始化信號量
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
Thread thread = new Thread(Method);
thread.Start("線程" + i);
}
semaphore.Release(2);//釋放信號量2個
Console.WriteLine("主線程運行完畢!");
Console.Read();
}
//線程執行方法
private static void Method(object o)
{
semaphore.WaitOne();//等待信號量
Thread.Sleep(1000);
Console.WriteLine(o.ToString());
semaphore.Release();//釋放信號量
}
}
其它的線程只有等到主線程釋放才會執行,因為我給信號量計數器的初始值是0,所以其它線程在主線程釋放前都會被阻塞。而後,我在主線程直接用Release(2)函數將計數器置為2,所以2個線程可以同時得到執行。
注意:可以給信號量設置一個名稱,這個名稱是操作系統可見的,因此,可以使用這些信號量來協調跨進程邊界的資源使用。
class Program
{
static void Main(string[] args)
{
//初始信號量5個,最多信號量10個
Semaphore seamphore = new Semaphore(5, 10, "Test");
seamphore.WaitOne();//等待信號
Console.WriteLine("獲取信號量 1");
seamphore.WaitOne();//等待信號
Console.WriteLine("獲取信號量 2");
seamphore.WaitOne();//等待信號
Console.WriteLine("獲取信號量 3");
Console.WriteLine("主線程運行完畢!");
Console.Read();
}
}
運行兩個這樣的程序,結果如下,在第二個運行的示例中,會將阻塞在第三個信號量上: