国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

泛型實現(xiàn)中沒有正確lock引用類型的一個隱藏bug分析

2019-11-17 03:08:42
字體:
供稿:網(wǎng)友

泛型實現(xiàn)中沒有正確lock引用類型的一個隱藏bug分析

最近看到這篇文章dotNetDR_的回復(fù),讓我想起一個真實發(fā)生的案例,下面就簡單說說這個關(guān)于lock引用類型的一個不容易發(fā)現(xiàn)的隱藏缺陷。

某類庫中的代碼,封裝了很簡單的一個通用類,用于線程安全地執(zhí)行某一種類型的特定方法,幾行代碼搞定:

    public class ConcurrentObjectExecutor<T> where T : IDisposable, new()    {        public void Start()        {            T obj = new T();            lock (obj)            {                Console.WriteLine(obj.ToString());                //do sth            }        }    }

設(shè)計這個類,估計本來主要是針對繼承自特定接口的類型能夠線程安全地執(zhí)行某一個方法。如果泛型類型T為類(class),則程序運行沒有任何問題,也能保證線程安全。

但是我們知道泛型約束只有繼承自接口和new還遠遠不能保證T就是一個class,結(jié)構(gòu)(struct)也可以繼承接口,也可以new。比如自定義一個結(jié)構(gòu):

    struct OrderMessger : IDisposable    {        public void Dispose()        {        }    }

下面的代碼可以編譯通過,運行時也不會拋出異常(如果不看上下文,多數(shù)調(diào)用者估計就這么讓它過去,很可能成為今后一個潛在的隱藏很深的bug):

    var executor = new ConcurrentObjectExecutor<OrderMessger>();     executor.Start();

很顯然,上面的泛型程序看上去是lock了一個結(jié)構(gòu),也就是鎖定了一個值類型。但是我們知道,lock關(guān)鍵字指定的鎖定對象必須是引用類型。上面的示例中,實際情況是將結(jié)構(gòu)實例obj隱式轉(zhuǎn)換成了一個引用對象實例(也就是裝箱,每次裝箱都生成了一個新的實例),這樣的lock是毫無意義的,因為在多線程的條件下,線程爭用的obj已經(jīng)不是指定的同一個實例(的引用)。

比較搞笑的是,直接寫下面的代碼,編譯時直接在lock語句上報告有錯誤(編譯時檢測真是幫了大忙,ms為什么不把泛型檢測搞的更智能些?):

           var obj = new OrderMessger();            lock (obj)            {            }

錯誤 1 “OrderMessger”不是 lock 語句要求的引用類型

解決的方法也很簡單,泛型約束在原來的基礎(chǔ)上再限定必須是class即可。

最后總結(jié)下:類庫設(shè)計中,越是通用的東西考慮的應(yīng)用場景越要周到,其中線程安全是非常重要必不可少的一個環(huán)節(jié),線程安全實現(xiàn)過程中,如果對多線程同步原語理解不夠深刻,很可能設(shè)計出有潛在缺陷的實現(xiàn),MSDN關(guān)于Thread Safe的調(diào)用和說明值得類庫開發(fā)者深刻學(xué)習(xí)和借鑒。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 灌云县| 北京市| 忻州市| 瑞安市| 南乐县| 社旗县| 南汇区| 若尔盖县| 高密市| 永安市| 景洪市| 新晃| 东明县| 精河县| 宁阳县| 进贤县| 清丰县| 泽州县| 于田县| 临桂县| 沛县| 白河县| 新干县| 青神县| 诸暨市| 定兴县| 普格县| 乌兰县| 岱山县| 万安县| 乐平市| 缙云县| 广德县| 响水县| 扎赉特旗| 金门县| 修武县| 庆元县| 饶河县| 循化| 宁化县|