1. 程式人生 > >c#之多線程之為所欲為

c#之多線程之為所欲為

輸出結果 結果 同時 str png index 寫法 use strong

一 什麽是多線程

1、 什麽是進程?一個 exe 運行一次就會產生一個進程,一個 exe 的多個進程之 間數據互相隔離。

2、 一個進程裏至少有一個線程:主線程。我們平時寫的控制臺程序默認就是單線程的,代 碼從上往下執行,一行執行完了再執行下一行;

3、 什麽是多線程:一個人兩件事情同時做效率高。同一時刻一 個人只能幹一件事情,其實是在“快速頻繁切換”,如果處理不當可能比不用多線程效率還低

二 Thread 對象

2.1 thread基礎寫法

public static void ThreadTest()
        {
            int a = 0;
            Thread thread1 = new Thread(m=> 
            {
                for (int i = 0; i < 20; i++)
                {
                    a = a + 1;
                    Console.WriteLine("線程1:"+ a);
                }

            });

            Thread thread2 = new Thread(m =>
            {
            for (int i = 0; i < 20; i++)
            {
                    a = a + 1;
                Console.WriteLine("線程2:"+ a);
                }

            });
            
            thread1.Start();
            thread2.Start();
            Console.ReadKey();
        }

這段代碼輸出結果如下:

可以看出兩個子線程啟動後是並行執行的,所以輸出結果沒有按照順序來

技術分享圖片

2.2 設置線程的優先級

thread1.Priority=ThreadPriority。。。

2.3 t1.Join()當前線程等待 t1 線程執行結束,實例如下:

這段代碼執行過後輸出的結果就是正常的從1輸出到了40

public static void ThreadTest()
        {
            int a = 0;
            Thread thread1 = new Thread(m=> 
            {
                
for (int i = 0; i < 20; i++) { a = a + 1; Console.WriteLine("線程1:"+ a); } }); Thread thread2 = new Thread(m => { //等待thread1線程任務完成後在執行 thread1.Join();
for (int i = 0; i < 20; i++) { a = a + 1; Console.WriteLine("線程2:"+ a); } }); //可以將參數傳入到子線程中 thread1.Start(a); //thread1.Join(); 或者將Join放在這裏 thread2.Start(a); Console.ReadKey(); }

2.4 Interrupt方法

Interrupt 用於提前喚醒一個在 Sleep 的線程,Sleep 方法會拋出 ThreadInterruptedException 異常 代碼如下:

代碼輸出到9的時候線程會休眠8秒鐘,但是運行到主線程thread1.Interrupt()時,子線程會被喚醒,然後執行catch裏面的Console.WriteLine("線程被喚醒");之後接著從10開始輸出到2000。需要註意的是只有線程自身能讓自身休眠

public static void ThreadTest2()
        {
            Thread thread1 = new Thread(() =>
            {
                for (int i = 0; i < 2000; i++)
                {
                    if (i==10)
                    {
                        //喚醒線程之後會引發ThreadInterruptedException類型的異常,所以需要try catch
                        try
                        {
                            //子線程休眠8秒鐘
                            Thread.Sleep(8000);
                        }
                        catch (ThreadInterruptedException ex)
                        {
                            Console.WriteLine("線程被喚醒");
                        }
                    }
                    Console.WriteLine(i);
                }
            });
            thread1.Start();
            //提前喚醒在沈睡的子線程
            Thread.Sleep(3000);
            thread1.Interrupt();
            Console.ReadKey();
        }

  

三 線程池

3.1、線程池:因為每次創建線程、銷毀線程都比較消耗 cpu 資源,因此可以通過線程池進行 優化。線程池是一組已經創建好的線程,隨用隨取,用完了不是銷毀線程,然後放到線程池 中,供其他人用。

3.2、用線程池之後就無法對線程進行精細化的控制了(線程啟停、優先級控制等)

3.3、ThreadPool 類的一個重要方法:

  static bool QueueUserWorkItem(WaitCallback callBack)

  static bool QueueUserWorkItem(WaitCallback callBack, object state)

3.4、除非要對線程進行精細化的控制,否則建議使用線程池,因為又簡單、性能調優又更好。

//QueueUserWorkItem是一個靜態方法不需要New
public static void ThreadPool()
{
    System.Threading.ThreadPool.QueueUserWorkItem(m=> 
    {
        for (int i = 0; i < 1000; i++)
        {
            i++;
            Console.WriteLine(i);
        }
    });
    Console.ReadKey();
}

  

四 TPL風格的異步方法

TPL(Task Parallel Library)是.Net 4.0 之後帶來的新特性,更簡潔,更方便。現在在.Net 平臺下已經大面積使用。

註意方法中如果有 await,則方法必須標記為 async,不是所有方法都可以被輕松的標記 為 async。WinForm 中的事件處理方法都可以標記為 async、MVC 中的 Action 方法也可以標 記為 async、控制臺的 Main 方法不能標記為 async。 TPL 的特點是:方法都以 XXXAsync 結尾,返回值類型是泛型的 Task<T>。 TPL 讓我們可以用線性的方式去編寫異步程序,不再需要像 EAP 中那樣搞一堆回調、邏 輯跳來跳去了。

/TPL風格返回的Task<T> 泛型的數據
//await 關鍵字等待異步方法返回
public static async void Task()
{
    WebClient wc = new WebClient();
    string s= await wc.DownloadStringTaskAsync("http://www.baidu.com");
    Console.WriteLine(s);
    Console.ReadKey();
}
public static void Task2()
{
    WebClient wc = new WebClient();
    //若果不使用await關鍵字就得使用Task<string>類型來接收數據
    Task<string> s2 = wc.DownloadStringTaskAsync("http://www.baidu.com");
    Console.ReadKey();
}

  

自己編寫一個TPL風格的異步方法:

使用了async關鍵字就必須返回Task泛型數據類型的數據

public static Task<string> StringAsync()
{
   return Task.Run(() =>
    {
        Thread.Sleep(5000);
        return "hehe";
    });
          
}
// GET: Home
public async Task<ViewResult> Index()
{

    var s = await StringAsync();
    return View();
}

  

如果返回值就是一個立即可以隨手可得的值,那麽就用 Task.FromResult() 如果是一個需要休息一會的任務(比如下載失敗則過 5 秒鐘後重試。主線程不休息,和 Thread.Sleep 不一樣),那麽就用 Task.Delay()。 3、Task.Factory.FromAsync()把 IAsyncResult 轉換為 Task,這樣 APM 風格的 api 也可以用 await 來調 用。 4、編寫異步方法的簡化寫法。如果方法聲明為 async,那麽可以直接 return 具體的值,不再用創建 Task,由編譯器創建 Task:

static async Task<int> F1Async() 
{ 
     return 1;
 } 
 
static Task<int> F2Async() 
{  
     return Task.FromResult(3); 
} 
 
static Task<int> F3Async() 
{  
     return Task.Run(()=> {  
     return 1 + 3;  }); 
}

一定要讓 async 的傳染性(調用異步方法要用 await,用了 await 方法就要聲明為 async,調 用我這個 async 方法的地方必須要 await……)不要輕易直接調用 Task 的 Wait、WaitAll 等方 法。等待一個用 await,而不是 task.Wait();等待多個用 await Task.WhenAll(),而不是 Task.WaitAll()

c#之多線程之為所欲為