C#編程 線程,任務和同步(2) 開啓線程

建立線程的幾種方法:

1 異步委託

建立線程的一種簡單方式是定義一個委託,並異步調用它。 委託是方法的類型安全的引用。Delegate類 還支持異步地調用方法。在後臺,Delegate類會建立一個執行任務的線程。編程

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace _016_線程_委託方式發起線程
{
    class Program
    {
        //通常咱們會爲比較耗時的操做 開啓單獨的線程去執行,好比下載操做
        static int Test(int i, string str)
        {
            Console.WriteLine("test:" + i + str);
            Thread.Sleep(100);//讓當親線程休眠(暫停線程的執行) 單位ms
            return 100;
        }
        static void Main(string[] args)
        {//在main線程中執行 一個線程裏面語句的執行 是從上到下的
            // 經過委託 開啓一個線程
            Func<int, string, int> a = Test;
            // 開啓一個新的線程去執行 a所引用的方法 
            IAsyncResult ar = a.BeginInvoke(100, "   mytest", null, null);
            // IAsyncResult 能夠取得當前線程的狀態
            Console.WriteLine("main");
            while (ar.IsCompleted == false)//若是當前線程沒有執行完畢
            {
                Console.Write(".");
                Thread.Sleep(10); //控制子線程的檢測頻率
            }
            int res = a.EndInvoke(ar);//取得異步線程的返回值
            Console.WriteLine(res);
        }
    }
}

輸出結果:安全

 

上面是經過循環檢測判斷線程是否結束。當咱們經過BeginInvoke開啓一個異步委託的時候,返回的結果是IAsyncResult,咱們能夠經過它的AsyncWaitHandle屬性訪問等待句柄。這個屬性返回一個WaitHandler類型的對象,它中的WaitOne()方法能夠等待委託線程完成其任務,WaitOne方法能夠設置一個超時時間做爲參數(要等待的最長時間),若是發生超時就返回false。多線程

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace _016_線程_委託方式發起線程
{
    class Program
    {
        //通常咱們會爲比較耗時的操做 開啓單獨的線程去執行,好比下載操做
        static int Test(int i, string str)
        {
            Console.WriteLine("test:" + i + str);
            Thread.Sleep(100);//讓當親線程休眠(暫停線程的執行) 單位ms
            return 100;
        }
        static void Main(string[] args)
        {//在main線程中執行 一個線程裏面語句的執行 是從上到下的
            // 經過委託 開啓一個線程
            Func<int, string, int> a = Test;
            IAsyncResult ar = a.BeginInvoke(100, "   mytest", null, null);// 開啓一個新的線程去執行 a所引用的方法 
            // IAsyncResult 能夠取得當前線程的狀態
            Console.WriteLine("main");
            //檢測線程結束
            bool isEnd = ar.AsyncWaitHandle.WaitOne(1000);//1000毫秒錶示超時時間,若是等待了1000毫秒 線程尚未結束的話 那麼這個方法會返回false 若是在1000毫秒之內線程結束了,那麼這個方法會返回true
            if (isEnd)
            {
                int res = a.EndInvoke(ar);
                Console.WriteLine(res);
            }
        }
    }
}

等待委託的結果的第3種方式是使用異步回調。在BeginInvoke的第三個參數中,能夠傳遞一個知足AsyncCallback委託的方法,AsyncCallback委託定義了一個IAsyncResult類型的參數其返回類型是void。對於最後一個參數,能夠傳遞任意對象,以便從回調方法中訪問它。異步

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace _016_線程_委託方式發起線程
{
    class Program
    {
        //通常咱們會爲比較耗時的操做 開啓單獨的線程去執行,好比下載操做
        static int Test(int i, string str)
        {
            Console.WriteLine("test:" + i + str);
            Thread.Sleep(100);//讓當親線程休眠(暫停線程的執行) 單位ms
            return 100;
        }

        static void Main(string[] args)
        {//在main線程中執行 一個線程裏面語句的執行 是從上到下的
            Console.WriteLine("main");
            //經過回調 檢測線程結束
            Func<int, string, int> a = Test;
            // 倒數第二個參數是一個委託類型的參數,表示回調函數,就是當線程結束的時候會調用這個委託指向的方法 倒數第一個參數用來給回調函數傳遞數據
            IAsyncResult ar = a.BeginInvoke(100, "   mytest", OnCallBack, a);
            Console.ReadKey();
        }

            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+"在回調函數中取得結果");
            }

    }
}

 在第三種方法中,咱們可使用Lamba表達式異步編程

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace _016_線程_委託方式發起線程
{
    class Program
    {
        //通常咱們會爲比較耗時的操做 開啓單獨的線程去執行,好比下載操做
        static int Test(int i, string str)
        {
            Console.WriteLine("test:" + i + str);
            Thread.Sleep(100);//讓當親線程休眠(暫停線程的執行) 單位ms
            return 100;
        }
        static void Main(string[] args)
        {
            Console.WriteLine("main");

            //經過回調 檢測線程結束
            Func<int, string, int> a = Test;
            a.BeginInvoke(100, "siki", ar =>
            {
                int res = a.EndInvoke(ar);
                Console.WriteLine(res + "在lambda表達式中取得");
            }, null);

            Console.ReadKey();
        }
    }
}

 

2 經過Thread類

 咱們能夠經過Thread類來建立線程,咱們構造一個thread對象的時候,能夠傳遞一個靜態方法,也能夠傳遞一個對象的普通方法。咱們先傳遞一個靜態方法:函數

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace _017_線程_經過Thread發起線程 {
    class Program {
        // 注意:線程調用的方法定義中,參數要用object
        static void DownloadFile(object filename)
        {
            // Thread.CurrentThread.ManagedThreadId表示當前線程ID
            Console.WriteLine("開始下載:"  +Thread.CurrentThread.ManagedThreadId +filename);
            Thread.Sleep(2000);
            Console.WriteLine("下載完成");

        }
        static void Main(string[] args) {
            //建立線程,傳入要執行的方法
            Thread t = new Thread(DownloadFile);
            // 開啓線程,若是線程調用的方法有參數,在Start中傳入
            t.Start("test");
            Console.WriteLine("Main");
            Console.ReadKey();

            // 使用Lamba表達式方法
            //Thread t = new Thread(() =>
            //{
            //    Console.WriteLine("開始下載:" + Thread.CurrentThread.ManagedThreadId);
            //    Thread.Sleep(2000);
            //    Console.WriteLine("下載完成");
            //});
            //t.Start();
        }
    }
}

輸出結果:this

 

下面咱們傳遞一個對象的普通方法,先定義一個類:spa

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace _017_線程_經過Thread發起線程 {
    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(2000);
            Console.WriteLine("下載完成");
        }

    }
}

而後建立線程:線程

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace _017_線程_經過Thread發起線程 {
    class Program {
        static void Main(string[] args) {

            MyThread my = new MyThread("xxx.bt","http://www.xxx.bbs");
            Thread t = new Thread(my.DownFile);
            t.Start();
        }
    }
}

 

3 使用線程池

建立線程須要時間。 若是有不一樣的小任務要完成,就能夠事先建立許多線程 , 在應完成這些任務時發出請求。 這個線程數最好在須要更多的線程時增長,在須要釋放資源時減小。3d

不須要 本身建立線程池,系統已經有一個ThreadPool類管理線程。 這個類會在須要時增減池中線程的線程數,直到達到最大的線程數。 池中的最大線程數是可配置的。 在雙核 CPU中 ,默認設置爲1023個工做線程和 1000個 I/o線程。也能夠指定在建立線程池時應當即啓動的最小線程數,以及線程池中可用的最大線程數。 若是有更多的做業要處理,線程池中線程的個數也到了極限,最新的做業就要排隊,且必須等待線程完成其任務。

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

     線程池中的全部線程都是後臺線程 。 若是進程的全部前臺線程都結束了,全部的後臺線程就會中止。 不能把入池的線程改成前臺線程 。     

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

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace _018_線程_線程池 {
    class Program {
        // 注意:使用線程池添加的執行函數,必需要有一個object類型的參數,即時不用
        static void ThreadMethod(object state)
        {
            Console.WriteLine("線程開始:"+Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(2000);
            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();

        }
    }
}

輸出結果:

 

4 經過任務建立

在.NET4 新的命名空間System.Threading.Tasks包含了類抽象出了線程功能,在後臺使用的ThreadPool進行管理的。任務表示應完成某個單元的工做。這個工做能夠在單獨的線程中運行,也能夠以同步方式啓動一個任務。     任務也是異步編程中的一種實現方式。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace _019_線程_任務 {
    class Program {
        static void ThreadMethod() {
            Console.WriteLine("任務開始");
            Thread.Sleep(2000);
            Console.WriteLine("任務結束");
        }
        static void Main(string[] args) {
            // 經過Task建立
            Task t = new Task(ThreadMethod);
            t.Start();

            // 經過任務工廠建立
            //TaskFactory tf = new TaskFactory();
            //Task t = tf.StartNew(ThreadMethod);

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

輸出結果:

 

連續任務:若是一個任務t1的執行是依賴於另外一個任務t2的,那麼就須要在這個任務t2執行完畢後纔開始執行t1。這個時候咱們可使用連續任務。

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

 任務層次結構:咱們在一個任務中啓動一個新的任務,至關於新的任務是當前任務的子任務,兩個任務異步執行,若是父任務執行完了可是子任務沒有執行完,它的狀態會設置爲WaitingForChildrenToComplete,只有子任務也執行完了,父任務的狀態就變成RunToCompletion

static void Main(){
	var parent = new Task(ParentTask);
	parent.Start();
	Thread.Sleep(2000);
	Console.WriteLine(parent.Status);
	Thread.Sleep(4000);
	Console.WriteLine(parent.Status);
	Console.ReadKey();
}
static void ParentTask(){
	Console.WriteLine("task id "+Task.CurrentId);
	var child = new Task(ChildTask);
	child.Start();
	Thread.Sleep(1000);
	Console.WriteLine("parent started child , parent end");
}
static void ChildTask(){
	Console.WriteLine("child");
	Thread.Sleep(5000);
	Console.WriteLine("child finished ");
相關文章
相關標籤/搜索