.Net Task的用法(一)線程


在了解Task用法之前就不得不先對線程有一定的了解

線程的創建

static void Main(){
    new Thread(Go).Start();  // new 一個Thread后要調用Start() 線程才會開始執行
    Task.Factory.StartNew(Go); //立即創建並執行 
    Task.Run(new Action(Go)); // 立即創建並執行
}
 
public static void Go(){
    Console.WriteLine("我是另一個線程");
}

線程的創建是比較占用資源,所以有了線程池,new 一個Thread 不會通過線程池(當然也可以使用ThreadPool),Task默認直接使用線程池

static void Main() {
    Console.WriteLine("我是主線程:Thread Id {0}", Thread.CurrentThread.ManagedThreadId);
    ThreadPool.QueueUserWorkItem(Go);
 
    Console.ReadLine();
}
 
public static void Go(object data) {
    Console.WriteLine("我是另一個線程:Thread Id {0}",Thread.CurrentThread.ManagedThreadId);
}

傳入參數

static void Main() {
    new Thread(Go).Start("arg1"); // 沒有匿名委托之前,我們只能這樣傳入一個object的參數
 
    new Thread(delegate(){  // 有了匿名委托之后...
        GoGoGo("arg1", "arg2", "arg3");
    });
 
    new Thread(() => {  // 當然,還有 Lambada
        GoGoGo("arg1","arg2","arg3");
    }).Start();
 
    Task.Run(() =>{  // Task能這么靈活,也是因為有了Lambda呀。
        GoGoGo("arg1", "arg2", "arg3");
    });
}
 
public static void Go(object name){
    // TODO
}
 
public static void GoGoGo(string arg1, string arg2, string arg3){
    // TODO
}

返回值

Thead是不能返回值的,但是作為更高級的Task當然要彌補一下這個功能

static void Main() {
    // GetDayOfThisWeek 運行在另外一個線程中
    var dayName = Task.Run<string>(() => { return GetDayOfThisWeek(); });
    Console.WriteLine("今天是:{0}",dayName.Result);
}

共享數據以及線程安全

線程直接可以通過靜態變量來共享數據

private static bool _isDone = false;
private static object _lock = new object();
static void Main(){
    new Thread(Done).Start();
    new Thread(Done).Start();
    Console.ReadLine();
}
 
static void Done(){
    lock (_lock){
        if (!_isDone){
            Console.WriteLine("Done"); // 猜猜這里面會被執行幾次?
            _isDone = true;
        }
    }
}

lock的資源沒有釋放之前其他線程是無法訪問其中的代碼塊的,_lock 是靜態的,所以,當一個線程進入后,其他的線程都需要等待

上述例子通過了靜態變量實現了2個線程之間的數據共享

Semaphore

它可以控制對某一段代碼或者對某個資源訪問的線程的數量,超過這個數量之后,其它的線程就得等待,只有等現在有線程釋放了之后,下面的線程才能訪問。這個跟鎖有相似的功能,只不過不是獨占的,它允許一定數量的線程同時訪問。

static SemaphoreSlim _sem = new SemaphoreSlim(3);    // 我們限制能同時訪問的線程數量是3
static void Main(){
    for (int i = 1; i <= 5; i++) new Thread(Enter).Start(i);
    Console.ReadLine();
}
 
static void Enter(object id){
    Console.WriteLine(id + " 開始排隊...");
    _sem.Wait();
    Console.WriteLine(id + " 開始執行!");         
    Thread.Sleep(1000 * (int)id);              
    Console.WriteLine(id + " 執行完畢,離開!");     
    _sem.Release();
}

異常處理

在使用Thread時,子線程中的異常,主線程是捕獲不到的,但是Task是可以的

public static void Main(){
    try{
        var task = Task.Run(() => { Go(); });
        task.Wait();  // 在調用了這句話之后,主線程才能捕獲task里面的異常
 
        // 對於有返回值的Task, 我們接收了它的返回值就不需要再調用Wait方法了
        // GetName 里面的異常我們也可以捕獲到
        var task2 = Task.Run(() => { return GetName(); });
        var name = task2.Result;
    }
    catch (Exception ex){
        Console.WriteLine("Exception!");
    }
}
static void Go() { throw null; }
static string GetName() { throw null; }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM