日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

并行设计模式

發布時間:2024/4/13 asp.net 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 并行设计模式 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
有關多線程的設計模式,有關多線程的設計模式呢,單例,不變模式,Future模式,和生產者消費者模式

設計模式是軟件工程當中的一個概念,它是指在軟件設計當中,普遍存在的或者反復出現的一種通用解決方案,最早是由Eric Gamma,1990年從建筑學當中引入到計算機當中的,設計模式來源于建筑學,建筑算是一個比較高深的一門領域,建筑是把數學和藝術相融合的一門學科,其實我們看到我們周圍的很多學科,很少有一門學科說,能夠通過兼具科學的性質,并且兼具藝術的性質,藝術的東西,舞蹈唱歌,建筑是一個很奇特的領域,你的好看,還得堅固,在這個建筑里面呢,他有些小的模式讓你去套,相對比較成熟優雅的設計,這個概念引入到軟件工程當中也是類似的,有的時候我們為了解決某個問題,為了解決某個問題呢,我們就把常用的結構,常用的組件就給抽出來,然后把它整理出來之后呢,我們每一次遇到問題以后,我們就去組件當中拿數據,那這個模型,套到我們的應用當中去,這就不需要我們每次重新思考說,這個我要怎么做才能避免這個問題,還有設計模式當中已經幫你解決這個方案,Gof是四人幫,Gang of Four,可復用面向對象軟件的基礎這個書,收錄了23種設計模式,這個書很早了,90年代了,都是由C++作為描述語言的,比較有名的就是觀察者設計模式,策略模式,裝飾者模式,模板方法,他抽取的都是組件,根據不同的用意,在什么場景下去使用的,有些模式是為了解決某些特定的問題,比如說享元,有些大的對象,可能需要反復的復用,他就為了解決這么一個性能問題,有些模式可能會更加廣泛一些,比如像模板方法,模板方法在有一些書里面歸類為源模式,基本的物質我們就稱為源什么,就有模板方法的身影,可大可小的一個概念

我們從廣義上來說,我們認為是一個范圍比較小的,相對比較微觀的一個組件,從廣義上來講呢,設計模式有架構模式,比如MVC模式,MVC模式其實并沒有給出說,V和C必須要是什么樣的結構,他沒有給出一個具體實現的一個東西,是思想上的一個東西,分層的模式也是一樣,我們在做設計的時候也是一樣,可以把一個程序分為幾層,每層之間是有依賴的,我們可以把中間某一層完全給抽掉,把它抽象出來,這些模式的范圍比較大,更加強調的是一種思想,所以可以認為是一種架構上的一種模式,另外設計模式就是我們剛剛說的,狹義上的模式,提取了一些小組件,然后不停的去復用這些組件,更加微觀層面上的呢,就是代碼模式,這是一種低層次的,是和編碼直接相關的,不同的語言有不同的寫法,比如JAVA當中equals的實現,如果你要去重寫equals的話,不同的人可能會有不同的寫法,但是我們可能有一套大家都公認的,還算不錯的寫法在那邊,我們有一套寫法來規范這個函數,怎么書寫,還有雙重檢查模式,雙重檢查模式的一種實現,那我就這么寫就可以了,我不認雙重檢查在JAVA中是一種好的模式,我只是拿出來舉一個例子,規定了代碼要怎么寫,最微觀上的一種模式,那在我們這里說的模式呢,基本上是屬于下面兩類,要么就是一種可復用的組件,要么就是告訴你代碼就是這么寫就可以了,其他就不用想了

之所以把單例拿出來,單例是非常重要的,同時他也是和多線程相關的,單例的特點就是我整個系統只有一個,在有多線程的程序當中,你怎么樣讓這個單例,能夠同時被多線程正常的去訪問,而不至于說,有多個線程之后呢,你這個線程創建了一個,你那個線程又創建了另外一個,單例用于全局對象,只有一個實例,比如系統全局性的一些配置

單例一個最簡單的實現呢,首先我們給他一個static的一個修飾,表示這個instance是和類相關的,然后我們在getInstance方法呢,也是一個static方法,當你去調用getInstance方法的時候,那你自然就可以拿到唯一的實例,因為static本身,他在初始化的時候,這個類第一次訪問的時候,他就會被初始化,這個是由虛擬機來保證的,所以就可以確定這個類是惟一的,當你有多個線程訪問的時候呢,這個類的初始化函數,它是線程安全的,但是這個模式有個小小的問題,何時產生實例呢,不太好控制,一般來說沒有意外的話呢,getInstance的時候,但是他實際產生實例的時間呢,Single對象第一次方法的時候

對象當中有其他的字段,比如status,如果我們訪問Singleton.STATUS呢,這個實例也會被創建,如果比較在意這個問題呢,就會有些問題,只是因為你很不小心的去訪問這個字段,而導致這個單例被初始化出來

我們可以使用同步的方法,我們在第一次getInstance的時候呢,我們再去生成這個類的實例,他這樣做的一個好處呢,我只有在getInstance第一次訪問的時候,我會把這個類的實例給生出來,除此之外呢,我如果有其他的字段在這個類當中呢,對他進行訪問呢,我這個類的實例是不會被生成的,所以這是一種延遲加載機制,只有當我真的要的時候,我才會去創建,同時為了防止多線程進入getInstance,從而創建多個實例呢,我們這里用synchronized進行修飾,當你有一個線程進來的時候,其他線程是進不來的,因此當你一個線程進來,判斷是否為null的時候,如果他為null,就表示其他線程確實沒有進來,因為同時只有一個線程能夠進來,當創建單例并且返回之后呢,后面的線程會發現呢,instance就被復制了,就不會創建LazySingleton,就直接返回了,這個是延遲加載的一個典型,他有一個問題呢,在你高并發的時候呢,大家都要去拿synchronized這個鎖,那我們知道這個東西他可能呢,對性能產生一定的影響,這里只是簡單的做了一個等于,然后return了,并沒有太多耗時間的操作,所以對性能影響并不會特別的大,讓你高頻率訪問的時候呢,或多或少會因為synchronized,產生一些影響的,因為這并不是一個高效的關鍵字,所以我們還有一種更加的方法使用單例

這個方法呢,首先它是延遲加載的,當你第一次訪問getInstance的時候呢,他才會創建static Singleton對象的實例,如果這個對象有其他字段,你訪問了其他字段呢,這個實例是不會被創建起來的,因為這個實例的初始化是定義在了,一個類的靜態內部類當中,很顯然靜態內部類是不會被初始化的,所以還是可以起到一個延遲加載的作用,只有當你getInstance的時候,這個時候你才訪問了靜態內部類,這樣做的好處很顯然,我們把synchronized關鍵字給去掉了,這個相對要比較好一些,單例的構造必須是private,如果構造函數不是單例,那就得不到很好的控制,任何人都可以去newinstance出來,你不能保證他一定是一個單例,當構造函數是private的時候呢,別人就不能去new instance了

下面我們來講一個不變模式,不變模式對多線程來講是非常重要的,不變模式是一個什么樣的狀態呢,一個類他在創建之后,他的內部狀態就不再發生改變了,不會隨著這個類的生命周期而改變了,比如一個坐標,坐標x,y被創建之后,在這個對象實例的整個使用當中,x永遠是1,y永遠是1,不會發生x和y的變化,為什么說不變模式,對多線程是非常重要的呢,因為不變模式不會被修改,它是一個只讀的對象,對于一個只讀對象來說呢,它是不需要同步的,而我們多線程之間呢,可能會進行各種各樣的同步,同步可能是非常耗時的,消耗資源的,提供非常好的性能,如果要在多線程不停的訪問,我們不妨可以把它設置為不變的東西

怎么樣保證這個類是沒辦法被修改的,首先我們的字段聲明為final,這個是符合JAVABEAN的一些規范,get字段的時候使用get方法,這里沒有set,因為你不需要set,因為Product這個類是不變的,他從創建之后,他就不會發生改變,Product另外一個特點呢,是全部聲明為final,為什么要必須聲明為final呢,因為final表示為常量,因為他只能進行一次賦值,一次賦值之后呢,不能進行二次賦值了,如果不是final,在他賦值之后呢,我是不是還有可能再進行修改,final相對來時說一個比較保守的做法,保證這個字段永遠都不可能被修改的,我什么時候對他進行賦值呢,在這個對象被創建的時候,你對他賦值,他一旦被創建完之后,它所有的字段就不會再修改,在這個構造函數當中進行賦值,一旦賦值完了,因為是final的,同時我整個類也聲明為final,你不可能去繼承我這個類,這是為什么呢,這個不是非常強烈的,你這個類不使用final問題也不是很大,但是有了這個final呢,你可以 保證他沒有子類,這意味著說,不可能會出現一個沒有Product的子類,而那個子類它是可變的,而根據里氏代換原則呢,我子類替代我的父類,如果你子類是可變的,而你父類是不可變的,并不是你在所有的場合,你都能夠替代她的父類,所以只有當我把它聲明為final之后,我讓他不可能有子類,我可以保證所有的product都是不可變的

下面我們來看不變模式的案例,其實不變模式在JAVA中也是比較普遍的,我們最常用的String他就是一個不變的對象,我們可以看到String對象呢,他一旦被創建之后,他不會再發生改變了,不管你是JDK6,7,8,String他都是不變的,這是一個最基本的,有些人可能會有疑惑,其實很多看起來像String的操作呢,他其實都是在后面生成一個String做的,而不是在老的String上做修改,這個就是String模式的特點,你如果對他做一個修改,你并不是把這個對象給改掉,而是生成一個新的對象,來替代原來老的對象,這個是不變模式的特點,除了String之外,我們很多跟基本類型相對應的類,布爾,Byte,他都是不變的,這里也是要強調一下,有時候我們會寫這樣的代碼,我們聲明了一個Integer i = 0,然后我們會做一個i++,這個時候看起來是i做了++操作,其實大家知道Integer是一個不變對象之后呢,其實這樣的操作顯然大家知道,i其實并沒有改變,而內部i++是怎么做的呢,他必定是生成了一個新的Integer對象,然后把新的對象變成多少呢,變成1,然后把1替換到原來i的位置上去,也就把i給替換掉,i++之后,Integer的引用本身都會發生變化,這一切都封裝到自動裝箱和自動拆箱當中,如果把i++匯編之后就可以看得非常清楚,這里也要強調一些不便模式的思想,你只要對不變的對象做任何的操作呢,對象本身是一定不會發生改變的,如果你看他好像被改變了,那一定是他生成了一個新的對象實例,不是在原有的對象上做修修補補,如果他在原有對象上做修修補補,他就沒有辦法保證他在多線程上的安全性了

下面我們來看一下Future模式,是一個使用非常廣泛的模式,現在我們來簡單了解一下Future模式,Future模式是什么意思呢,他的核心是異步調用,客戶端要向服務端調用一個程序,服務程序需要做某些事情,構造某一個數據,但這個數據構造很慢,要做很久,做完之后他把數據返回,返回之后再返回給客戶端,這里模擬的是函數調用,然后這個時候客戶端就做其他的事情了,可以看到這個函數調用呢,我們發起端的那個人,他可能要等一段時間,他要等,如果是Future模式它會變成什么樣子呢,我客戶端如果要去調用某一個數據,那我就給服務端發送一個請求,可能就是一個函數調用,但是這個時候不一樣,花了好久才返回,這里立即返回,數據的構造是需要花很長時間的,你立即返回數據在哪里呢,其實你返回的是一個空的東西,什么都沒有,這個數據并沒有構造出來,只是返回給你說,我給你一張契約,這就好比說,我們在網上買東西,我們下了一個訂單,我下了訂單之后呢,貨不可能馬上就到,但是你憑著這張訂單,你在未來就可以收到,你要的東西,萬一他沒有發給你,萬一他發給你了你就把快遞收下了,那就沒什么事情了,但是萬一你沒有拿到貨,那你憑著這張訂單,可以質疑那個商家說,那我就憑著這個收據,我要求你把這個東西給我,這里也是這個意思,你這個操作是馬上返回的,但是你貨沒有,我只給你一個空箱子,只是給你一個訂單,在將來如果你要拿到真實的數據,那你可以在將來的某一個時間段,你憑著我給你的訂單,你把這個貨拿出去,也就是Future模式呢,Future指的就是未來,就像是訂單一樣的,雖然一張契約,兩者簽了一個協議,我承諾以后會給你這個東西,但是我現在拿不出來,那你先把承諾書帶走,先做其他的事情,因為我現在沒有,先把其他的活干掉,不用等著我這個東西,等到某個時間段需要這個東西的時候呢,憑著我這個單子,我這個契約書,你把我這個貨拿走,你可以在這里做一些額外的事情,對于實際的數據構造者來講呢,他在這里慢慢的做,然后客戶端可以拿到,就憑著這個訂單把數據拿到,這個思想就是Future模式的思想,你不需要立即去得到他

下面我們來看一下Future模式的一個簡單的實現,這里Future模式的結構當中,我們有一個接口Data,Data就是我們要拿到的數據,FutureData相當于訂單,他不是一個真實的數據,但是他和真實數據之間共享一個接口,因此我們在整個使用當中呢,我們可以使用接口來代替數據,我只要給你一個Data就可以了,你不用管我是realData還是FutureData,RealData可能很慢,所以我可能等不及,所以你拿到一個真實的實例呢,很有可能是一個FutureData,也就是訂單本身,接著你在未來的某一個階段,你可以通過FutureData拿RealData,FutureData當中呢,去包含RealData,看到這兩個關系沒有,FutureData會聚合一個realData,對于Future模式的實現核心就是這個,ClientThread我用一個線程裝配起來,核心是這三個,真實數據是構造的很慢的,FutureData是很快的,因為他只是構造一個空殼而已,在未來某個時間段,這個RealData裝配完之后呢,把它設置到FutureData里面去

下面我們來看一下具體的實現,首先我們data是一個接口,他能得到某一個數據,要返回的是一個String,FutureData里面會聚合一個RealData,這個是真實要返回給別人的,我整個的包一層,因為我立即要返回的是FutureData,當我去拿到getResult的時候呢,我要看一下我這個realData是不是已經ready了,如果沒有ready呢,那我當前線程拿的線程要做等待,因為你不能繼續往下走,因為我這個數據還沒有準備好,直到什么時候你可以往下走呢,我真實數據被設置進來之后,裝配進來之后,你才能往下走,所以當真實數據被裝配進來之后呢,我isReady就會設置我為true,同時會通知,數據已經準備好了,你可以繼續往下走了

對于RealData來講,我們簡單的做了一個模擬,我們RealData返回一個字符串,我們RealData構造的很慢,我們特意加了一些sleep方法,模擬一次很慢的構造,這個構造函數,構造的實例,是很慢的,根據data這個接口,getResult方法,把這個result給返回

那我們來看看這個client,這個client請求一個數據的時候,他直接構造的是FutureData,首先明確返回值是Data接口,你不能返回RealData,因為RealData是一下子返回不了的,他需要快速返回,只能返回Data,它會先構造一個FutureData,FutureData構造是很快的,然后在一個新的線程當中,去構造這個RealData,因為RealData構造是很慢的,那我必須把它異步執行,線程構造很慢沒有關系,我可以慢慢做,但是我FutureData一旦構造完成之后呢,我馬上就返回了,但是這個返回的FutureData,當其他線程拿到這個返回值之后,它里面的東西很有可能是空的,他對象實例是有了,他要的東西是沒有的,但是這個時候沒有關系,在未來某一個時間,如果真的需要result的話,需要future.getResult,把這個數據給拿出來,如果當時的數據已經構造完了,他這個result拿到數據,否則他就會等待,他才會把這個數據拿到,這就是Future模式的一個核心

我們可以做一些業務邏輯上的事情,我們不需要去拿result,如果request拿到data之后,我們馬上就去拿getResult呢,這個地方一定會被阻塞,一定會被阻塞,真實數據還沒有構造完,線程就陷入等待,知道你notifyAll被調用了,但是如果你在這個地方去做其他的事情,做完之后再去拿getResult,那你有可能馬上就返回了,這個數據馬上就被構造了,這里是怎么把一個同步調用阻塞調用,變成一個異步的調用

Future模式是很常用的,JDK當中也是把Future模式拿進來了,做了一個包裝和實現,這里就是對Future模式的一個包裝和實現了,首先他在這個地方有一個Future,Future和Runnable,Future可以去拿某一個數據,另外一個接口是RunnableFuture,重要的我們看FutureTask,FutureTask它是一個帶有Future性質的Runnable,他可以被一個線程帶起來,Future相當于一個訂單而已,FutureTask可以把線程調起來,另外一個很重要的呢,Callable,Callalbe是什么呢,他其實和Runnable很接近,他也可以直接構造成一個線程,但是他的本質不同呢,Callable是有返回值的,也就這么點區別,Runnable返回值void,當你用Runnable,你線程做某一個動作,Callable做某一個線程,并且告訴這個線程說,做完這個動作之后呢,你必須給我一個返回值,是一個模板類,可以返回任意的類型,事實上你在使用Future模式的時候呢,你既可以使用Future,在JDK當中你既可以使用Future,也可以使用Callable,都是可以的,在使用Future的時候呢,本質上使用FutureTask,FutureTask他本身是一個Runnable,同時也是一個Future,它是帶有Future功能的一個Runnable的接口,現在我們使用Callable來實現

剛才說的RealData,之前在我們自己的小小框架當中呢,是繼承了Future,現在我讓他實現Callable,Callable他有個方法call,他就這么一個方法,他就返回你要的String,在我這個RealData當中呢,當然這個返回是很慢的,模擬一個時間比較長的調用,當你使用RealData Callable接口,來構造一個線程的時候呢,這個線程的執行呢,可能是比較慢的,線程的返回String要花一些時間

Callable接口來實現Future模式,首先我們通過Callable接口,去生成一個FutureTask,FutureTask它是一個Runnable,同時他也是一個Future,它是帶有Future功能的Runnable,他有Future功能是能帶返回值,然后你把Runnable提交給線程池,線程就會返回,就會執行,執行之后你通過Future,因為他有Future功能,FutureTask他有Future的功能,通過Future的getResult方法,把這個數據給查出來,這一整套實現可以看到,相對來說還是比較好用的,使用了JDK當中的功能,線程池,FutureTask,我們要把RealData給實現一下,這個是和業務邏輯相關的東西,然后把整個給串起來,這里是模擬了一個其他業務邏輯的操作,然后把這個數據拿到就行了,如果你在future.get的時候數據還沒有準備好,這個地方同樣會陷入等待,等待這個數據準備好之后,你再去把這個數據給拿出來,這個就是另外一個更加簡便的實現,這里我們并沒有構造FutureTask,實際上也沒有這個必要

因為Executor支持使用Callable來構造,在這種情況之下呢,Callable支持返回值,這個返回值使得我們submit方法呢,他可以返回一個Future對象,因此這樣寫就可以拿到Future,然后在未來某一個時間段呢,你可以通過future.get拿到東西,這里把Callable傳進去,這兩種方法都是可以的,我們只是希望在線程調用當中拿到一個返回值,這個場景也是非常常用的,我們并不是做完事情就好了,希望拿到一個東西,他其實可以把一個同步的調用,變成異步調用,同步調用太慢了,這就是一個很好的方法,而且使用起來也是比較容易的

生產者消費者模式,它是一個非常經典的多線程模式,我兩個線程之間如何去共享數據,我線程A要給線程B數據,意味著我線程A,要知道線程B的存在,那同樣的道理,我線程A要去線程B拿數據,我線程B要知道線程A的存在,但是他符合我們的松散耦合的原則,這個系統要盡可能的保持松散,你多線程之間彼此不要知道彼此的存在的,你知道的東西越少越好,你不要知道這個知道那個,就相當于耦合性太大,生產者可能有好多個,有各種各樣的生產者,消費者也有可能有好多個,所以這也不是一件很現實的事情,所以我們就要想一個辦法說,我能不能給一個公共的區間,公共的區域,所有人都往這個區間寫數據,所有人都往這里去拿數據,這樣所有的生產者,產生數據,產生請求的,處理這些數據,處理這些請求的,彼此之間不需要知道彼此的存在,軟件工程的想法呢,如果說一個模塊,他對外知道的應該是越少越好,最好是一無所知,如果他對外一無所知,我外界的程序不管你怎么改,對我都是沒有影響的,就比較符合我們說的開閉原則,一個模塊的修改應該是保持閉合,那么在這種情況之下呢,可能有各種類型的生產者,他們之間也不需要認識,往公共緩沖區里面放數據,緩沖區我們可以使用BlockQueue阻塞隊列的方式來實現,所有的消費者也是一樣,他也不需要知道生產者的存在,他也往緩沖區拿數據,就可以了

這個就是生產者和消費者的草圖,在這里各種各樣的生產者,各種各樣的消費者,彼此之間都是不相關的,不知道他們之間是有存在的,生產者只往里面放,放內容放東西,所有生產者只知道緩沖區,消費者也只知道緩沖區,他一根筋的往外拿,生產者消費模式就建立起來了,最適合使用生產者消費者模式的呢,其實就是BlockingQueue,他不是一個高性能的實現,如果需要的是一個高性能的生產者消費者模式,它是不適合的,我自己去實現一個內存緩存區,ConcurrentLinkedQueue,它是一個高并發的實現,高并發的隊列,進行包裝來實現生產者消費者,如果你使用BlockingQueue,只是一個容易合理的實現,但是他不是一個性能很好的解決方案,但是在一般場景下也就夠用了

生產者提交任務,消費者提取任務,內存緩沖區是生產者消費者的一個核心,任務是緩沖區里面放的數據結構,緩沖區它是一個隊列

消費者包含了一個BlockingQueue,消費者要把BlockingQueue放到自己里面來,生產者也是一樣,生產者也把BlockingQueue放到隊列里面來,BlockingQueue里放的是什么東西呢,是PCData,這里放PCData數據結構,里面是數據,生產者產生一個數據請求,BlockingQueue里面去,消費者拿到這個數據請求,這里就是生產者消費者的一個實現

生產者會往隊列里放數據,這個Queue就是BlockingQueue,BlockingQueue有很多種,有基于數組的,有基于鏈表的,BlockingQueue他本身是一個接口,我們只要構造一個實例就行了,offer會往這里放一個對象,把數據構造出來,然后消費者就能提取數據,take方法提取數據,提取完之后進行計算,while循環不停的往里放數據,不停的取數據,BlockingQueue的好處是什么呢,如果Queue當中為空,消費者沒有放進來,消費者比生產者快很多,這個數據為空,這個消費者就會在take阻塞,這是一個Blocking的一個Queue,阻塞隊列,這個時候消費者會掛起,做一個等待,并不會拼命的往下讀,這個是BlockingQueue的特點,也是因為這樣,稱不上是一個高并發的方式,真正高并發是不會做等待,就是拼命的檢查有沒有數據,如果你這個隊列真的有很多數據的話,真的有大量的阻塞進來的話,你就應該把這個循環結束掉

?

總結

以上是生活随笔為你收集整理的并行设计模式的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。