程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 【轉】C#異步編程及其同步機制,

【轉】C#異步編程及其同步機制,

編輯:C#入門知識

【轉】C#異步編程及其同步機制,


C#異步編程及其同步機制

本篇文章涵蓋一下幾部分內容:

1. 什麼是異步編程,為什麼會需要異步編程

2. .NET下的異步編程及其發展

3. .NET線程同步機制及線程間數據封送

4. 異步模式

5. 線程安全及異常處理

6. 線程取消

什麼是異步編程,為什麼會需要異步編程

這個世界上資源是受限的。但資源限制和懶惰一樣促進了工業和科技的發展。在計算機方面舉個例子,計算機非得是二進制嗎?對計算機來說二進制最好嗎?不是,這是由於當時工業水平限制,把電壓分成兩份表示0和1比分成三份更加方便且可靠;虛擬內存管理,Cache等技術都是由當時硬件條件所限逼出來的技術,同樣異步編程和分布式編程也是。生活中的好多事物都不是線性的,拿學生時代的一個常見的例子說一下,明天開學,海量作業一點沒寫,於是找個同學作業抄一下,但在短時間內一個人很難抄得完,於是我花錢請了幾個同學一起抄,把一份工作分給幾個人去做,這就是異步了。但除去筆跡不同這麼做沒有一個人抄安全,有可能哥幾個把一份內容重復抄了好幾遍(線程安全),這期間萬一筆,紙,橡皮沒准備充分還得有一個資源爭用,死鎖的問題(同步的問題),哥幾個抄得時候還會相互報一下各自進度(線程間數據封送),所以說這麼干是有風險的我們就得有個機制避免這種風險的發生,異步編程和這個類似。

那在編程中異步會用在什麼地方呢?一個簡單情形,圖形界面程序,後台如果要連接數據庫查詢或寫入海量數據或者進行I/O操作,界面會“假死”。之所以發生這種情況是這些處理都在UI線程中,這些操作占用UI線程時,任何拖動UI,點擊按鈕等操作都得不到及時響應。解決的方法是將這些需要長時間的操作放入一個新的線程異步操作,把UI線程解放出來。其它的應用比如海量數據計算,服務器響應客戶端請求等等。

.NET下的異步編程及其發展

首先說明一點,線程可以分為前台線程和後台線程。前台線程和吸血鬼差不多很恐怖,要想干死進程,就必須把所有的前台進程都干掉,UI線程就是前台線程。而後台線程就是二房生的兒子了,進程消亡後緊跟著死掉了,很明顯的後台線程就是Word的拼寫檢查,或者outlook負責跟服務器同步更新郵件的線程。

任何平台和編程語言都會有多線程的實現機制和方法。對於C#來講Thread類就是創建線程,管理線程的一種最初始的手段。但是創建和銷毀一個線程是很耗費資源的,而且創建的線程越多,線程間切換就越頻繁(計算機CPU個數受限),線程切換也要耗費資源和時間,再加上線程管理是一件很費心的事,所以微軟就引入了線程池的概念。線程池是一個先進先出FIFO的隊列,程序員只需要把操作或者任務丟給線程池,讓.NET framework替程序員管理線程,線程復用等,極大的簡化了開發。這裡就有一個控制線程池內線程數量的問題。線程池內的線程肯定得根據需要動態變化,但適應這種需要的算法是什麼呢?

一個簡單的算法:往線程池中增加一些線程,觀察線程池的吞吐量,如果增加後吞吐量增加,說明線程不夠,需要增加線程。但這存在一個問題,對於一個很大的任務需要長時間占用線程,增加線程並不能增加吞吐量,此時如果增加線程會加重負擔。所以在CLR v4時引入了本地隊列(Local Queue)的概念,如果一個線程內創建了另一個線程,新創建的線程不再丟給全局隊列,而是給本地隊列排隊等候調用。這就又有個問題,如果一個隊列內任務執行完了,而另一個隊列還有好多怎麼辦?那就讓執行完任務的本地隊列從該隊列上“偷“一個線程執行。這樣達到負載均衡。當然線程池的算法會隨著CLR版本升級而不斷演進,更加智能的管理線程。對普通開發者而言可以不用考慮這些細節,無縫的體驗線程池帶來的便利和效率就行了。

線程池如此方便,我們怎麼使用線程池呢?可以通過以下幾種方式:

通過類方法ThreadPool.QueueUserWorkItem直接調用。

通過.net Framework 4.0 引入的TPL(Task Parallel Library)任務並行庫。

TPL中最主要的兩個類是Task和Parallel。而新版C++標准中也引入了類似的概念parallel_for, parallel_foreach, parallel_invoke等。

詳細信息見以下鏈接。

通過異步委托(BeginInvoke/EndInvoke)調用。

通過BackgroundWorker, BackgroundWorker是WinForm, WPF下的一個控件,主要用於提供UI控件下的協作式取消,進度報告等。

這裡我還要講一下PFX(Parallel FrameWork)。PFX從概念上可以分為數據並行和任務並行。

上層的由兩個結構化數據並行APIs組成:PLINQ和Parallel類。而底層的任務並行包含了Task 類和一系列的附屬結構用於幫助並行編程。注意PFX是建立在線程池之上的,是更好使用線程池的一種途徑,有說法說是用TPL比直接使用線程池效率更高。關於PLINQ,Task,Parallel類及上圖所列結構的使用請參考一下鏈接。

.NET線程同步機制及線程間數據封送

首先.Net的同步機制是干什麼的?概況來講是為了安全。同步機制的存在是因為異步操作是不安全的,會帶來一系列的問題,這些問題在第一章節中已經討論過了。而線程間數據封送和COM與.Net framework數據封送一樣,是為了線程間數據和狀態的傳遞。

那麼.net的同步機制有哪些呢?概括一下:

簡單的鎖定方法:Thead類的Sleep, Join等以及Task的Wait方法。

基於對象的鎖定:

lock(Monitor.Enter/Monitor.Exit):首先強調一下它不可以跨進程間線程同步。一般跨進間線程同步都有一個特征,就是同步對象都有名字。

Mutex和Semophore(slim):這兩個都可以跨進程同步,兩者的區別在於:Mutex只能有一等待資源,而Semophore可以有多個。拿廁所舉例,Mutex相當於廁所中只有一個蹲位,只能一個上了才能上另一個,而Semophore可以有多個蹲位,可以讓多個線程同時阻塞一個線程的執行。就是n個哥們一起蹲著,又來一哥們,然後這n個哥們就占著那啥不那啥。

Reade/Writer 鎖。

3.基於信號

事件等待句柄AutoResetEvent, ManualResetEvent(Slim):注意這兩個也是允許跨進程的,兩者用法差不多,使一個線程釋放一個信號從而使得其他線程能夠執行。

CountdownEvent(4.0被引入):這個和上邊用法正好相反,它使得一個線程等待收到其他線程的信號後再執行。

Barrier

Wait and pulse

4. 非阻塞的同步結構

Thread.MemoryBarrier

Thread.VolatileRead/Write

Interlocaked

關於以上同步機制具體應用和Demo代碼請參考以下鏈接。

而關於線程間數據封送,一個很好的例子就是點擊button後開始在新線程中執行某個操作,但執行過程需要在一個label上顯示出來,這時候就需要把新線程內表示執行狀態的數據對象封送回UI線程。這部分內容可以參考我以前寫的一篇帖子:http://www.cnblogs.com/salomon/archive/2012/06/28/2567339.html。

異步模式

什麼需要異步模式?所謂模式,其實是一種方法,就跟上篇博客裡所講的,是從工程實踐中總結出來的解決相似或特定問題的一種慣用手段。常見的異步模式包括:

APM模式: BeginXXX/EndXXX, IAsyncResult

EAP模式(基於事件的異步模式)

Windows Form

MethodNameAsync

Event

TAP(基於任務的異步模式)

MethodNameAsync

Task/Task<Result>

這部分內容以下鏈接講得很好了,感興趣可以看一下。更詳盡的介紹去MSDN或者官方網站上去找相似的文檔。

線程安全及異常處理

新線程中拋出的異常會不會自動封送到主線程中?如何處理新線程中拋出的異常?什麼是線程安全?怎樣做到線程安全?

線程取消

正在執行的線程怎麼能不能取消,怎麼取消合適?暴力取消?協作式取消?

C#5.0新的異步模式Async和await關鍵字

請參考我以前的博客:http://www.cnblogs.com/salomon/archive/2012/06/27/2565862.html

原文鏈接:http://www.cnblogs.com/salomon/archive/2012/07/26/2610548.html

本篇文章來源於黑基網-中國最大的網絡安全站點 原文鏈接:http://www.hackbase.com/tech/2012-08-03/66710.html

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved