前言:這篇部落客要是在上一篇部落格的基礎上,重新複習了一下上一篇部落格的內容,講解了一些特殊的使用,並且使用了大量的案例來說明,主要說明了非同步委託的使用和回呼函數執行非同步委託以及跨線程訪問控制項的解決方案和一個雙色球的案例。這篇文章我以代碼的形式寫了,裡面有大量的注釋,我們可以詳細的研究,很高興能和你們一起研究。
1.線程的複習
(1) 多線程Thread調用委託方法的實現Demo
class Program
{
static void Main(string[] args)
{
Thread thread = new Thread(new ThreadStart(DemoMethod));
//設定線程參數
thread.IsBackground = true; //後台線程,常用的都是後台線程
//設定線程的名字
thread.Name = "給開發人員用的";
//設定線程的優先順序(只是提議)
thread.Priority = ThreadPriority.Highest; //0-31
//啟動線程,只是告訴作業系統線程準備好,開始執行此線程,但是並不一定線程立即執行
thread.Start();
Thread.Sleep(3000);
Console.ReadKey();
}
private static void DemoMethod()
{
Console.WriteLine("執行的背景工作執行緒是:{0}", Thread.CurrentThread.ManagedThreadId); ;
}
}
執行結果是:執行的背景工作執行緒是:10
(2)線程配合Lambda運算式的使用
static void Main(string[] args)
{
//第一種寫法
Thread thread = new Thread(new ThreadStart(() =>
{
Console.WriteLine("執行的背景工作執行緒是:{0}", Thread.CurrentThread.ManagedThreadId); ;
}));
//第二種寫法
new Thread(() =>
{
Console.WriteLine("執行的背景工作執行緒是:{0}", Thread.CurrentThread.ManagedThreadId); ;
}).Start();
//設定線程參數
thread.IsBackground = true; //後台線程,常用的都是後台線程
//設定線程的名字
thread.Name = "給開發人員用的";
//設定線程的優先順序(只是提議)
thread.Priority = ThreadPriority.Highest; //0-31
//啟動線程,只是告訴作業系統線程準備好,開始執行此線程,但是並不一定線程立即執行
thread.Start();
Thread.Sleep(3000);
Console.ReadKey();
}
//執行結果是:
//執行的背景工作執行緒是:4
//執行的背景工作執行緒是:3
(3)帶參數的線程執行
static void Main(string[] args)
{
//定義帶參數的線程
Thread thread = new Thread(new ParameterizedThreadStart(ParameterMethodDemo));
//開啟線程,傳遞參數
thread.Start("韓迎龍");
Thread.Sleep(3000);
}
private static void ParameterMethodDemo(object obj)
{
Console.WriteLine("執行一個帶參數的線程方法:{0}", obj);
}
//執行結果:執行一個帶參數的線程方法:韓迎龍
(4)非同步委託回呼函數的使用
namespace ReviewThread
{
//定義委託
internal delegate int AddDel(int a,int b);
class Program
{
static void Main(string[] args)
{
//定義委託變數的時候,如果是指向第一個方法,那麼必須使用new關鍵字
AddDel del = new AddDel(Demo);
//同步調用
//int res = del(5, 3);
//非同步呼叫,本質就是重新開啟一個線程幫我們,執行委託的方法
//內部:開啟一個新的線程執行委託
IAsyncResult result = del.BeginInvoke(2, 5, null, null);
//執行主線程要乾的事情
//非同步委託的好處是:不阻塞主線程
int methodRes = del.EndInvoke(result);
Console.WriteLine(methodRes);
Console.ReadKey();
}
public static int Demo(int a, int b)
{
return a + b;
}
//執行結果:7
}
}
2. 委託的非同步呼叫:由另一個線程執行委託的方法
(1) 簡單的非同步委託調用Demo
namespace AsyncDelegate
{
internal delegate int AddDemoDel(int a,int b);
class Program
{
static void Main(string[] args)
{
Console.WriteLine("當前主線程是:{0}", Thread.CurrentThread.ManagedThreadId);
AddDemoDel addDemoDel = new AddDemoDel(AddDemo);
//委託的非同步呼叫
//開始調用委託方法,自動建立一個線程並執行委託的方法
IAsyncResult result = addDemoDel.BeginInvoke(4, 5, null, null);
//得到委託的傳回值
int inta = addDemoDel.EndInvoke(result); //阻塞當前主線程,只等到子線程執行完成,並返回方法的傳回值之後繼續執行
Console.WriteLine("主線程執行完畢,結果是" + inta);
Thread.Sleep(10000);
Console.ReadKey();
}
private static int AddDemo(int a, int b)
{
Thread.Sleep(3000);
Console.WriteLine("當前執行的線程是:{0}", Thread.CurrentThread.ManagedThreadId);
return a + b;
}
//執行結果:
//當前主線程是:9
//當前執行的線程是:10
//主線程執行完畢,結果是9
}
}
(2)設定一個回呼函數執行非同步委託
namespace AsyncDelegate
{
internal delegate int AddDemoDel(int a,int b);
class Program
{
static void Main(string[] args)
{
Console.WriteLine("當前主線程是:{0}", Thread.CurrentThread.ManagedThreadId);
AddDemoDel addDemoDel = new AddDemoDel(AddDemo);
//指定回呼函數,處理的非同步委託
addDemoDel.BeginInvoke(3, 4, new AsyncCallback(DelCallBackFuncDemo), "韓迎龍");
Console.WriteLine("主線程執行完畢");
Console.ReadKey();
}
//定義一個委託單額回調方法
public static void DelCallBackFuncDemo(IAsyncResult arr)
{
Console.WriteLine("回呼函數執行的線程是:{0}", Thread.CurrentThread.ManagedThreadId);
//當我們的委託方法執行完成後,會自動調用次回呼函數
AsyncResult result=(AsyncResult)arr;
Console.WriteLine("在回調方法裡面擷取到的傳遞過來的狀態:{0}",result.AsyncState);
//回呼函數擷取當前委託方法執行的結果
AddDemoDel del = (AddDemoDel)result.AsyncDelegate; //將非同步委託轉成我們自訂的委託類型
int funResult = del.EndInvoke(result); //擷取方法的傳回值
Console.WriteLine("擷取到當前方法執行的結果是:{0}", funResult);
}
private static int AddDemo(int a, int b)
{
Thread.Sleep(3000);
Console.WriteLine("當前執行的線程是:{0}", Thread.CurrentThread.ManagedThreadId);
return a + b;
}
}
}
//執行結果:
3. 一個雙色球變化的案例
(1) 建立一個Windows表單應用程式(Winform),起名為DoubleBallDemo,拖放一個Button控制項
(2)項目Demo代碼結果如下:
namespace DoubleBallDemo { //定義設定標籤值的委託類型 public delegate void SetLbTextDel(string str,Label label); public partial class Form1 : Form { List<Label> listlabel = new List<Label>(); public bool ISStop = true; //是否結束,改變Label的值 public Form1() { InitializeComponent(); //設定當前的按鈕的名字為開始 btnStartEnd.Text = "開始"; //初始化6個Label標籤 for (int i = 0; i < 6; i++) { Label lblDemo = new Label(); lblDemo.Text = "0"; lblDemo.AutoSize = true; //自動調整大小 lblDemo.Location = new Point(10 + i * 50, 50);//在X軸上每隔50放一個控制項 //第一將當前控制項放到表單裡面去 Controls.Add(lblDemo); //往當前集合變數裡面添加當前標籤 listlabel.Add(lblDemo); } } private void btnStartEnd_Click(object sender, EventArgs e) { //現在點擊的是開始按鈕 if (btnStartEnd.Text.Equals("開始")) { this.ISStop = false; //啟動一個線程,幫我們改變標籤的數字 Thread thread = new Thread(() => { Random rand = new Random(); while (!ISStop) { for (int i = 0; i < listlabel.Count; i++) { //listlabel[i].Text = rand.Next(0, 9).ToString(); //使用Invoke方法 //Invoke方法內部會沿著控制項建立的路徑往上找到建立此控制項的線程,找到線程之後會直接使用此線程調用委託方法 //誰建立的此標籤,那麼就由誰調用後面傳過來的委託 //標誌:標誌當前的控制項是否是當前線程建立的 if (listlabel[i].InvokeRequired) { listlabel[i].Invoke(new SetLbTextDel(SetLbText), rand.Next(0, 9).ToString(), listlabel[i]); } else { //如果當前控制項直接就是當前線程建立的那麼就可以直接存取就行了 SetLbText(rand.Next(0, 9).ToString(), listlabel[i]); } } Thread.Sleep(200); } //線程的方法執行完成後,會自動結束線程,線程的資源會自動釋放 }); thread.IsBackground = true; thread.Start(); //把當前按鈕的名字改成結束 btnStartEnd.Text = "結束"; } else { //停止線程,改變Laebl的資料 //盡量避免直接關閉一個線程:abort方法 //關閉線程,一般使用標誌。 this.ISStop = true; //把當前的按鈕改成開始 btnStartEnd.Text = "開始"; } } private static void SetLbText(string str, Label label) { label.Text = str; } } }
(2) 項目顯示: