天天看點

c#線程的使用

如何開啟一個線程擷取線程方法的傳回值

我們首先可以使用委托開啟線程

//定義我們的線程方法
        static int Test(int i,string str)
        {
            Console.WriteLine("test"+i+str);
            Thread.Sleep();//讓目前線程休眠(暫停線程的執行) 機關ms
            return ;
        }
           

在Main方法裡面通過BeginInvoke開啟線程,因為有些線程方法比較耗時,是以這裡我們可以使用異步傳回的方式檢測這個線程方法是否執行完畢

static void Main(string[] args) {
            Func<int, string, int> a = Test;
            IAsyncResult ar = a.BeginInvoke(, "siki", null, null);// 開啟一個新的線程去執行 a所引用的方法 
            IAsyncResult 可以取得目前線程的狀态
            可以認為線程是同時執行的(異步執行)
            Console.WriteLine("main");
            while (ar.IsCompleted == false)//如果目前線程沒有執行完畢
            {
                Console.Write(".");
                Thread.Sleep(); //控制子線程的檢測頻率
            }
            int res = a.EndInvoke(ar);//取得異步線程的傳回值
            Console.WriteLine(res);
}
           

當然除了異步檢測,我們同樣可以使用内置方法給定這個線程執行事件

通常我們會使用這個方法檢測網絡連接配接

bool isEnd = ar.AsyncWaitHandle.WaitOne();//1000毫秒表示逾時時間,如果等待了1000毫秒 線程還沒有結束的話 那麼這個方法會傳回false 如果在1000毫秒以内線程結束了,那麼這個方法會傳回true
            if (isEnd)
            {
                int res = a.EndInvoke(ar);
                Console.WriteLine(res);
            }
           

通過回調檢測線程結束

//定義我們回調函數需要執行的方法,主要進行一些判斷用來終止回調
        static void OnCallBack( IAsyncResult ar )
        {
            Func<int, string, int> a = ar.AsyncState as Func<int, string, int>;
            int res =  a.EndInvoke(ar);
            Console.WriteLine(res+"在回調函數中取得結果");
        }
        //調用委托開啟線程
        Func<int, string, int> a = Test;
            //倒數第二個參數是一個委托類型的參數,表示回調函數,就是當線程結束的時候會調用這個委托指向的方法 倒數第一個參數用來給回調函數傳遞資料,這裡就是将這個正在執行的線程方法傳遞過去
          IAsyncResult ar = a.BeginInvoke(, "siki", OnCallBack, a);// 開啟一個新的線程去執行 a所引用的方法 
           

當然我們既然可以寫回調方法,那麼我們就可以使用Lambda表達式書寫這個方法

a.BeginInvoke(, "siki", ar =>
            {
                int res = a.EndInvoke(ar);
                Console.WriteLine(res+"在lambda表達式中取得");
            }, null);
           

通過Thread類開啟線程(常用方法)

//寫一個我們的線程方法,注意如果是帶有參數的話必須要使用Object類型
//Thread.CurrentThread.ManagedThreadId這個東西是用來擷取目前線程的ID,以後可以通過ID做管理
      static void DownloadFile(object filename)
        {
            Console.WriteLine("開始下載下傳:"  +Thread.CurrentThread.ManagedThreadId +filename);
            Thread.Sleep();
            Console.WriteLine("下載下傳完成");

        }
           

下面使Thread方法開啟線程執行這個方法

可以看到我們在執行個體化Thread的時候就是在開啟線程,然後通過start方法執行這個線程,假如這個方法帶有參數的話,就直接在start開啟的時候直接傳遞參數就行了

static void Main(string[] args) {
            Thread t = new Thread(DownloadFile);//建立出來Thread對象,這個線程并沒有啟動
            t.Start("xxx.種子");//開始,開始去執行線程
            Console.WriteLine("Main");

}
           

同樣的我們将線程方法傳入Thread裡面說明這個構造函數本質也是一個委托,既然這樣我們就可以使用Lambda表達式構造

Thread t = new Thread(() =>
            {
                Console.WriteLine("開始下載下傳:" + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep();
                Console.WriteLine("下載下傳完成");
            });
            t.Start();
           

我們還可以通過對象進行開啟線程

首先我們建立一個類

class MyThread
    {
        private string filename;
        private string filepath;

        public MyThread(string fileName, string filePath)
        {
            this.filename = fileName;
            this.filepath = filePath;
        }

        public void DownFile()
        {
            Console.WriteLine("開始下載下傳"+filepath+filename);
            Thread.Sleep();
            Console.WriteLine("下載下傳完成");
        }

    }
           

然後我們再main方法裡面給這個類執行個體化并且用這個執行個體化的類開啟我們的線程,和直接綁定方法類似

MyThread my = new MyThread("xxx.bt", "http://www.xxx.bbs");//執行個體化
            Thread t = new Thread(my.DownFile);//我們構造一個thread對象的時候,可以傳遞一個靜态方法,也可以傳遞一個對象的普通方法
            t.Start();
           

前台線程和背景線程

前台線程就是我們通過Thread類開啟的線程,背景線程就是通過線程池開啟的線程,使用差別不大,但是背景線程會在前台線程全部執行完畢後被立即殺死,結束目前程式的運作

Thread t = new Thread(DownloadFile);//這個是前台線程
         t.IsBackground = true;//設定為背景線程 
           

線程池

使用線程池需要注意的事項:

線程池中的所有線程都是背景線程 。 如果程序的所有前台線程都結束了,所有的背景線程就會停止。 不能把入池的線程改為前台線程 。

不能給入池的線程設定優先級或名稱。

入池的線程隻能用于時間較短的任務。 如果線程要一直運作(如 Word的拼寫檢查器線程),就應使用Thread類建立一個線程。

static void ThreadMethod(object state)
        {
            Console.WriteLine("線程開始:"+Thread.CurrentThread.ManagedThreadId);//擷取目前線程ID
            Thread.Sleep();
            Console.WriteLine("線程結束");
        }
        static void Main(string[] args)
        {
            ThreadPool.QueueUserWorkItem(ThreadMethod);//開啟一個工作線程
            ThreadPool.QueueUserWorkItem(ThreadMethod);//開啟一個工作線程
            ThreadPool.QueueUserWorkItem(ThreadMethod);//開啟一個工作線程
            ThreadPool.QueueUserWorkItem(ThreadMethod);//開啟一個工作線程
            ThreadPool.QueueUserWorkItem(ThreadMethod);//開啟一個工作線程
            ThreadPool.QueueUserWorkItem(ThreadMethod);//開啟一個工作線程
            ThreadPool.QueueUserWorkItem(ThreadMethod);//開啟一個工作線程
            Console.ReadKey();//必須要有,不然當主線程結束後這些線程池的背景線程會被立即殺死

        }
           

通過任務開啟一個線程

static void ThreadMethod() {
            Console.WriteLine("任務開始");
            Thread.Sleep();
            Console.WriteLine("任務結束");
        }
      static void Main(string[] args) {
            //第一種,使用Task建立
            //Task t = new Task(ThreadMethod);//傳遞一個需要線程去執行的方法
            //t.Start();

            //第二種,使用TaskFactory
            TaskFactory tf = new TaskFactory();
            Task t = tf.StartNew(ThreadMethod);//這裡使用了工廠模式,可以通過tf一直開啟線程

            Console.WriteLine("Main");
            Console.ReadKey();
        }
           

注意,使用Task可以開啟連續任務,将這些人物串聯起來

static void DoFirst(){
    Console.WriteLine("do  in task : "+Task.CurrentId);
    Thread.Sleep();
}
static void DoSecond(Task t){
    Console.WriteLine("task "+t.Id+" finished.");
    Console.WriteLine("this task id is "+Task.CurrentId);
    Thread.Sleep();
}
Task t1 = new Task(DoFirst);
Task t2 = t1.ContinueWith(DoSecond);
Task t3 = t1.ContinueWith(DoSecond);
Task t4 = t2.ContinueWith(DoSecond);
           

通過ContinueWith這個方法我們可以給這些任務建立連接配接,這個例子就是,當ti的任務執行完成同時執行t2和t3,當t2執行完成才會執行t4;