亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

對(duì)象的延遲創(chuàng)建與多線程安全訪問(wèn)

系統(tǒng) 2068 0

.NET 4.0 多線程開(kāi)發(fā)系列之

對(duì)象的延遲創(chuàng)建與多線程安全訪問(wèn)

=========================

版權(quán)聲明:

本文作者金旭亮擁有此文的原創(chuàng)版權(quán),任何人均可以出于學(xué)習(xí)與交流目的在網(wǎng)絡(luò)中共享與傳播此文,但不得用于商業(yè)目的,比如用于出版技術(shù)書籍或者進(jìn)行以盈利為目的的商業(yè)培訓(xùn)。

另外,如有轉(zhuǎn)貼請(qǐng)注明出處。

有培訓(xùn)需求的單位請(qǐng)直接與本人聯(lián)系。

此聲明適用于本人在互聯(lián)網(wǎng)上發(fā)表的所有原創(chuàng)類型文章和相關(guān)的技術(shù)與教學(xué)資源。

====================================


1 使用多線程延遲創(chuàng)建“唯一”的對(duì)象

在實(shí)際開(kāi)發(fā)中,我們可能會(huì)希望將一個(gè)對(duì)象的創(chuàng)建延遲到需要真正用到它的時(shí)候。最典型的是使用數(shù)據(jù)庫(kù)連接對(duì)象訪問(wèn)數(shù)據(jù)庫(kù)。在分布式的軟件系統(tǒng)中,客戶端與服務(wù)器一般不會(huì)在同一臺(tái)計(jì)算機(jī)上,創(chuàng)建數(shù)據(jù)庫(kù)對(duì)象并啟動(dòng)到遠(yuǎn)程數(shù)據(jù)庫(kù)連接是一件比較耗廢系統(tǒng)資源的事情。當(dāng)然,通過(guò)編寫一些條件判斷語(yǔ)句我們可以實(shí)現(xiàn)“在需要的時(shí)候才創(chuàng)建對(duì)象”這一目標(biāo)。

以下是一段典型代碼 :

class Parent

{

A obj = null ;

public void VisitEmbedObject()

{

if (obj == null )

obj = new A ();

}

}

然而,當(dāng)延遲動(dòng)態(tài)創(chuàng)建的對(duì)象會(huì)被多線程共享訪問(wèn)時(shí),就麻煩了,想想上述代碼放在線程函數(shù)中,由多個(gè)線程同時(shí)執(zhí)行,如果不施加任何的同步手段, A 對(duì)象可能會(huì)被創(chuàng)建多個(gè)!這很容易理解,由于操作系統(tǒng)采用分時(shí)時(shí)間片的調(diào)度方法將 CPU 分配給特定線程執(zhí)行,因此完全可能發(fā)生某個(gè)線程還沒(méi)有執(zhí)行完畢,另一個(gè)線程又投入運(yùn)行的情況。

在本例中,有可能一個(gè)線程在創(chuàng)建對(duì)象過(guò)程中(而此時(shí) obj 仍是 null ),另一個(gè)線程嘗試訪問(wèn) obj 會(huì)發(fā)現(xiàn)它仍是 null ,于是又會(huì)創(chuàng)建另一個(gè) A 對(duì)象!

在過(guò)去,為了解決這個(gè)問(wèn)題,一般需要給多線程共享資源加鎖:

class Parent

{

A obj = null ;

public void VisitEmbedObject()

{

lock ( this )

{

if (obj == null )

obj = new A ();

}

//...

}

}

這個(gè)方法是傳統(tǒng)的編程方法。

然而,到了 .NET 4.0 ,有更簡(jiǎn)單更方便的方法達(dá)到同樣的目的。

2 泛型類 Lazy<T>

泛型類 Lazy<T> 位于 System 命名空間,是 .NET 4.0 新引入的。它的功能就是解決多線程運(yùn)行環(huán)境下的對(duì)象延遲創(chuàng)建問(wèn)題。

通過(guò)實(shí)例可以很清楚地掌握它的用法( Demo UseLazyExample )。

本例中我們定義了一個(gè)很簡(jiǎn)單的類型 A

class A

{

public A()

{

Console.WriteLine("A 對(duì)象創(chuàng)建,其標(biāo)識(shí): {0}",this.GetHashCode());

}

public int IntValue

{

get; set;

}

}

以下代碼實(shí)現(xiàn)了對(duì)象的延遲創(chuàng)建:

class Program

{

static void Main(string[] args)

{

Lazy<A> AObj = new Lazy<A>();

Console.WriteLine(" 現(xiàn)在將給 A 對(duì)象的 IntValue 屬性賦值 100");

A obj = AObj.Value; // 此處導(dǎo)致對(duì)象創(chuàng)建!

obj.IntValue = 100;

Console.WriteLine("A 對(duì)象 {0} IntValue 屬性 ={1}",

obj.GetHashCode(),obj.IntValue);

Console.ReadKey();

}

}

運(yùn)行結(jié)果如下:

<!-- [if gte vml 1]><v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"> <v:stroke joinstyle="miter" /> <v:formulas> <v:f eqn="if lineDrawn pixelLineWidth 0" /> <v:f eqn="sum @0 1 0" /> <v:f eqn="sum 0 0 @1" /> <v:f eqn="prod @2 1 2" /> <v:f eqn="prod @3 21600 pixelWidth" /> <v:f eqn="prod @3 21600 pixelHeight" /> <v:f eqn="sum @0 0 1" /> <v:f eqn="prod @6 1 2" /> <v:f eqn="prod @7 21600 pixelWidth" /> <v:f eqn="sum @8 21600 0" /> <v:f eqn="prod @7 21600 pixelHeight" /> <v:f eqn="sum @10 21600 0" /> </v:formulas> <v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect" /> <o:lock v:ext="edit" aspectratio="t" /> </v:shapetype><v:shape id="圖片_x0020_1" o:spid="_x0000_i1028" type="#_x0000_t75" style='width:267.75pt;height:92.25pt;visibility:visible;mso-wrap-style:square'> <v:imagedata src="file:///D:/Users/JINXUL~1/AppData/Local/Temp/msohtmlclip1/01/clip_image001.png" mce_src="file:///D:/Users/JINXUL~1/AppData/Local/Temp/msohtmlclip1/01/clip_image001.png" o:title="" /> </v:shape><![endif]--><!-- [if !vml]--><!-- [endif]-->

對(duì)象的延遲創(chuàng)建與多線程安全訪問(wèn)

上述代碼雖然實(shí)現(xiàn)了對(duì)象的延遲創(chuàng)建,但示例代碼運(yùn)行于單線程環(huán)境下,還沒(méi)有顯示出使用 泛型類 Lazy<T> 的好處。

3 多線程環(huán)境下使用泛型類 Lazy<T>

考慮一下新的編程場(chǎng)景:

現(xiàn)在有多個(gè)線程都在運(yùn)行中,這些線程都需要調(diào)用 A 類型的對(duì)象所提供的功能。我們希望只創(chuàng)建一個(gè) A 對(duì)象并且能讓多個(gè)線程安全地訪問(wèn)它。

請(qǐng)看示例 UseLazyInMultiThreadEnvironment 。這是一控制臺(tái)程序,以下代碼位于 Program 類中。

首先定義一個(gè)用于多線程共享的對(duì)象 AObj ,注意它使用 Lazy<T> 進(jìn)行了封裝:

static Lazy < A > AObj = null ;

緊接著定義一個(gè)用于創(chuàng)建 A 對(duì)象的工廠函數(shù):

static Func < A > valueFactory = delegate ()

{

Console .WriteLine( " 調(diào)用工廠函數(shù)創(chuàng)建 A 對(duì)象 " );

A obj = new A { IntValue = ( new Random ()).Next(1,100) };

return obj;

};

注意上面用到了 C# 中的匿名方法實(shí)現(xiàn)給委托變量賦值。之所以將這個(gè)方法稱為“工廠函數(shù)”,來(lái)自于《設(shè)計(jì)模式》一書中的“抽象類工廠”設(shè)計(jì)模式,簡(jiǎn)言之,可將負(fù)責(zé)創(chuàng)建特定類型對(duì)象的函數(shù)稱為“工廠”,創(chuàng)建出來(lái)的對(duì)象就是這個(gè)工廠的“產(chǎn)品”。

緊接著是一個(gè)線程函數(shù),將被多個(gè)線程同時(shí)執(zhí)行:

static void ThreadFunc()

{

Console.WriteLine(" 對(duì)象 {0} IntValue={1}",

AObj.Value .GetHashCode(), AObj.Value .IntValue);

}

注意:上面訪問(wèn)共享對(duì)象是通過(guò) Lazy<A> 進(jìn)行的,這是實(shí)現(xiàn)多線程同步的關(guān)鍵所在。

好了,以下是實(shí)驗(yàn)代碼:

static void Main(string[] args)

{

Console.WriteLine("/n 敲任意鍵開(kāi)始演示, ESC 退出 ...");

while (Console.ReadKey(true).Key != ConsoleKey.Escape)

{

Console.WriteLine();

// 注意將第 2 個(gè)參數(shù)改為不同的值:

//1 NotThreadSafe

//2 AllowMultipleThreadSafeExecution

//3 EnsureSingleThreadSafeExecution

// 運(yùn)行看看結(jié)果有何不同?

AObj = new Lazy<A>(valueFactory, LazyExecutionMode.EnsureSingleThreadSafeExecution);

for (int i = 0; i < 10; i++)

{

Thread th = new Thread(ThreadFunc);

th.Start();

}

}

}

}

上述代碼運(yùn)行時(shí),敲任意鍵將創(chuàng)建 10 個(gè)線程,這 10 個(gè)線程將嘗試訪問(wèn)同一個(gè) A 類型的對(duì)象。

這里要特別注意 Lazy<A> 的構(gòu)造函數(shù),它的第一個(gè)參數(shù)表示當(dāng)創(chuàng)建共享對(duì)象時(shí)要調(diào)用的工廠函數(shù),第二個(gè)參數(shù)對(duì)程序的執(zhí)行有著重大影響。以下是部分實(shí)驗(yàn)結(jié)果:

LazyExecutionMode. NotThreadSafe

<!-- [if gte vml 1]><v:shape id="圖片_x0020_4" o:spid="_x0000_i1027" type="#_x0000_t75" style='width:243.75pt;height:284.25pt;visibility:visible; mso-wrap-style:square'> <v:imagedata src="file:///D:/Users/JINXUL~1/AppData/Local/Temp/msohtmlclip1/01/clip_image003.png" mce_src="file:///D:/Users/JINXUL~1/AppData/Local/Temp/msohtmlclip1/01/clip_image003.png" o:title="" /> </v:shape><![endif]--><!-- [if !vml]--><!-- [endif]-->

對(duì)象的延遲創(chuàng)建與多線程安全訪問(wèn)

可以看到, 10 個(gè)線程運(yùn)行時(shí)創(chuàng)建了 3 個(gè)對(duì)象,不同線程得到的值可能相同也可能不同,而且程序執(zhí)行時(shí)對(duì)象創(chuàng)建的次數(shù)還會(huì)有變化。

LazyExecutionMode. AllowMultipleThreadSafeExecution

對(duì)象的延遲創(chuàng)建與多線程安全訪問(wèn)

<!-- [if gte vml 1]><v:shape id="圖片_x0020_7" o:spid="_x0000_i1026" type="#_x0000_t75" style='width:237.75pt; height:260.25pt;visibility:visible;mso-wrap-style:square'> <v:imagedata src="file:///D:/Users/JINXUL~1/AppData/Local/Temp/msohtmlclip1/01/clip_image005.png" mce_src="file:///D:/Users/JINXUL~1/AppData/Local/Temp/msohtmlclip1/01/clip_image005.png" o:title="" /> </v:shape><![endif]--><!-- [if !vml]--><!-- [endif]-->

可以看到,雖然在這種情況下 A 對(duì)象也創(chuàng)建了多個(gè),但多個(gè)線程最終訪問(wèn)的卻是同一個(gè)對(duì)象,這個(gè)同步是由 Lazy<A> 實(shí)現(xiàn)的。

LazyExecutionMode. EnsureSingleThreadSafeExecution

<!-- [if gte vml 1]><v:shape id="圖片_x0020_10" o:spid="_x0000_i1025" type="#_x0000_t75" style='width:213.75pt; height:236.25pt;visibility:visible;mso-wrap-style:square'> <v:imagedata src="file:///D:/Users/JINXUL~1/AppData/Local/Temp/msohtmlclip1/01/clip_image007.png" mce_src="file:///D:/Users/JINXUL~1/AppData/Local/Temp/msohtmlclip1/01/clip_image007.png" o:title="" /> </v:shape><![endif]--><!-- [if !vml]--><!-- [endif]-->

對(duì)象的延遲創(chuàng)建與多線程安全訪問(wèn)

可以看到,不管有多個(gè)線程, Lazy<A> 將保證只調(diào)用工廠函數(shù)一次,僅創(chuàng)建一個(gè) A 對(duì)象。

注意:

Lazy<T>僅能保證多線程訪問(wèn)的是同一個(gè)對(duì)象,但這并不是說(shuō)Lazy<T>能自動(dòng)同步對(duì)此共享對(duì)象的訪問(wèn)。這意味著您必須在線程函數(shù)中使用lock等線程同步手段避免“多線程訪問(wèn)共享資源導(dǎo)致數(shù)據(jù)存取錯(cuò)誤”現(xiàn)象的發(fā)生。

4 小結(jié):

.NET 4.0 .NET 歷史上一個(gè)重要的版本,引入了不少新的技術(shù),同時(shí)對(duì)原有的組件也進(jìn)行了更新,在后面的系列文章中,我將帶領(lǐng)大家再去探索 .NET 4.0 多線程開(kāi)發(fā)的另外一些實(shí)用的開(kāi)發(fā)技巧。

對(duì)象的延遲創(chuàng)建與多線程安全訪問(wèn)


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 2019中文字幕视频 | 97夜夜澡人人爽人人免费 | 国产国拍亚洲精品永久不卡 | 欧美日本国产 | 日本又黄又爽又色的免费视频 | 一级毛片播放 | 国产极品嫩模大尺度福利视频 | 日日碰日日摸日日澡视频播放 | 成人精品一区久久久久 | 久久亚洲精品玖玖玖玖 | 国产午夜永久福利视频在线观看 | 日韩美a一级毛片 | 91九色视频 | 国产欧美高清 | 99re66热这里只有精品17 | 亚拍一区| 国产福利视频一区 | 亚洲综合色在线 | 我要看欧美精品一级毛片 | 亚洲日本va中文字幕婷婷 | 免费日韩精品 | 亚洲国产伦理 | 精品免费国产一区二区女 | 免费观看黄a一级视频日本 免费观看黄色 | 欧美影视一区 | 国产激情一区二区三区四区 | 成人xxxx| 橘梨纱视频一区二区在线观看 | a毛片免费在线观看 | 欧美毛片在线播放观看 | 国产精品久久久久免费a∨ 国产精品久久久久免费视频 | 亚洲精品在线网 | 99热久久久这里只有精品免费 | 午夜私人 | 天天怕夜夜怕狠狠怕 | 国产高清在线精品免费 | 99热久久国产精品免费观看 | 最新日韩中文字幕 | 国产精品视频网站 | 精品国产91乱码一区二区三区 | 四虎看黄 |