天天看點

C#線程C#的線程(一)

線程是一個獨立的運作單元,每個程序内部都有多個線程,每個線程都可以各自同時執行指令。每個線程都有自己獨立的棧,但是與程序内的其他線程共享記憶體。但是對于.NET的用戶端程式(Console,WPF,WinForms)是由CLR建立的單線程(主線程,且隻建立一個線程)來啟動。在該線程上可以建立其他線程。

圖:

C#線程C#的線程(一)

多線程由内部線程排程程式管理,線程排程器通常是CLR委派給作業系統的函數。線程排程程式確定所有活動線程都被配置設定到合适的執行時間,線程在等待或阻止時 (例如,在一個獨占鎖或使用者輸入) 不會消耗 CPU 時間。

在單處理器計算機上,線程排程程式是執行時間切片 — 迅速切換每個活動線程。在 Windows 中, 一個時間片是通常數十毫秒為機關的區域 — — 相比來說 線程間互相切換比CPU更消耗資源。在多處理器計算機上,多線程用一種混合的時間切片和真正的并發性來實作,不同的線程會在不同的cpu運作代碼。

如:

在主線程上建立了一個新的線程,該新線程執行WrWrite2方法,在調用t.Start()時,主線程并行,輸出“1”。

C#線程C#的線程(一)

線程Start()之後,線程的IsAlive屬性就為true,直到該線程結束(當線程傳入的方法結束時,該線程就結束)。

在新線程和主線程上調用Go方法時分别建立了變量cycles,這時cycles在不同的線程棧上,是以互相獨立不受影響。

C#線程C#的線程(一)

如果不同線程指向同一個執行個體的引用,那麼不同的線程共享該執行個體。

新線程和主線程上調用了同一個執行個體的Go方法,是以變量i共享。

靜态變量也可以被多線程共享

如果将Go方法的代碼位置互換

如果新線程在Write之後,done=true之前,主線程也執行到了write那麼就會有兩個done。

不同線程在讀寫共享字段時會出現不可控的輸出,這就是多線程的線程安全問題。

解決方法: 使用排它鎖來解決這個問題--lock

當多個線程都在争取這個排它鎖時,一個線程擷取該鎖,其他線程會處于blocked狀态(該狀态時不消耗cpu),等待另一個線程釋放鎖時,捕獲該鎖。這就保證了一次

隻有一個線程執行該代碼。

Join可以實作暫停另一個線程,直到調用Join方法的線程結束。

線程t調用Join方法,阻塞主線程,直到t線程執行結束,再執行主線程。

Sleep:暫停該線程一段時間

上面的例子是使用Thread類的構造函數,給構造函數傳入一個ThreadStart委托。來實作的。

然後調用Start方法,來執行該線程。委托執行完該線程也結束。

多數情況下,可以不用new ThreadStart委托。直接在構造函數裡傳入void類型的方法。

使用lambda表達式

最簡單的就是在lambda表達式直接傳入參數。

或者在調用Start方法時傳入參數

此時傳給Thread的構造函數是有一個參數的void類型方法。

因為Thread的構造函數也可以傳遞如下委托:

Lambda簡潔高效,但是在捕獲變量的時候要注意,捕獲的變量是否共享。

因為每次循環中的i都是同一個i,是共享變量,在輸出的過程中,i的值會發生變化。

解決方法-臨時變量

這時每個線程都指向新的temp;在該線程中temp不受其他線程影響。

總結:

Thread構造函數傳遞方法有兩種方式:

預設情況下建立的線程都是Foreground,隻要有一個Foregournd線程在執行,應用程式就不會關閉。

Background線程則不是。一旦Foreground線程執行完,應用程式結束,background就會強制結束。

可以用IsBackground來檢視該線程是什麼類型的線程。

此時并不能在Main方法裡捕獲線程Go方法的異常,如果是Thread自身的異常可以捕獲。

正确捕獲方式:

當建立一個線程時,就會消耗幾百毫秒cpu,建立一些新的私有局部變量棧。每個線程還消耗(預設)約1 MB的記憶體。線程池通過共享和回收線程,允許在不影響性能的情況下啟用多線程。

每個.NET程式都有一個線程池,線程池維護着一定數量的工作線程,這些線程等待着執行配置設定下來的任務。

線程池線程注意點:

通過Thread.CurrentThread.IsThreadPoolThread.可以檢視該線程是否是線程池的線程。

使用線程池建立線程的方法:

Task

ThreadPool.QueueUserWorkItem

Asynchronous delegates

BackgroundWorker

TPL

Framework4.0下可以使用Task來建立線程池線程。調用Task.Factory.StartNew(),傳遞一個委托

Task.Factory.StartNew 傳回一個Task對象。可以調用該Task對象的Wait來等待該線程結束。

給Task構造函數傳遞Action委托,或對應的方法,調用start方法,啟動任務

直接調用Task.Run傳入方法,執行。

Task泛型允許有傳回值。

QueueUserWorkItem沒有傳回值。使用 QueueUserWorkItem,隻需傳遞相應委托的方法就行。

委托異步可以傳回任意類型個數的值。

使用委托異步的方式:

聲明一個和方法比對的委托

調用該委托的BeginInvoke方法,擷取傳回類型為IAsyncResult的值

調用EndInvoke方法傳遞IAsyncResulte類型的值擷取最終結果

EndInvoke做了三件事情:

等待委托異步的結束。

擷取傳回值

抛出未處理異常給調用線程

推薦使用回調函數來簡化委托的異步調用,回調函數參數為IAsyncResult類型

}

本文轉自zsdnr 51CTO部落格,原文連結:http://blog.51cto.com/12942149/1949804,如需轉載請自行聯系原作者