精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

警惕.NET中的匿名方法造成變量共享

開發(fā) 后端
.NET中的匿名方法是一柄雙刃劍,稍有不慎就會變成陷阱,本文將幫助大家避開這一陷阱。

匿名方法是.NET 2.0中引入的高級特性,“匿名”二字說明它可以把實(shí)現(xiàn)內(nèi)聯(lián)地寫在一個方法中,從而形成一個委托對象,而不用有明確地方法名,例如:

static void Test()
{
    Action action = delegate(string value)
    {
        Console.WriteLine(value);
    };

    action("Hello World");
}

但是匿名方法的關(guān)鍵并不僅于“匿名”二字。其最強(qiáng)大的特性就在于匿名方法形成了一個閉包,它可以作為參數(shù)傳遞到另一個方法中去,但同時也能訪問方法的局部變量和當(dāng)前類中的其它成員。例如:

class TestClass
{
    private void Print(string message)
    {
        Console.WriteLine(message);
    }

    public void Test()
    {
        string[] messages = new string[] { "Hello", "World" };
        int index = 0;

        Action action = (m) =>
        {
            this.Print((index++) + ". " + m);
        };

        Array.ForEach(messages, action);
        Console.WriteLine("index = " + index);
    }
}

如上所示,在TestClass的Test方法中,action委托調(diào)用了同在TestClass類中的私有方法Print,并對Test方法中的局部變量index進(jìn)行了讀寫。在加上C# 3.0中Lambda表達(dá)式的新特性,匿名方法的使用得到了極大的推廣。不過,如果使用不當(dāng),匿名方法也容易造成難以發(fā)現(xiàn)的問題。

問題案例

某位兄弟最近在一個簡單的數(shù)據(jù)導(dǎo)入程序,主要工作是從文本文件中讀取數(shù)據(jù),進(jìn)行分析和重組,然后寫入數(shù)據(jù)庫。其邏輯大致如下:

static void Process()
{
    List batchItems = new List();
    foreach (var item in ...)
    {
        batchItems.Add(item);

        if (batchItems.Count > 1000)
        {
            DataContext db = new DataContext();
            db.Items.InsertAllOnSubmit(batchItems);
            db.SubmitChanges();

            batchItems = new List();
        }
    }
}

每次從數(shù)據(jù)源中讀取數(shù)據(jù)后,添加到batchItems列表中,當(dāng)batchItems滿1000條時便進(jìn)行一次提交。這段代碼功能運(yùn)行正常,可惜時間卡在了數(shù)據(jù)庫提交上。數(shù)據(jù)的獲取和處理很快,但是提交一次就要花較長時間。于是想想,數(shù)據(jù)提交和數(shù)據(jù)處理不會有資源上的沖突,那么就把數(shù)據(jù)提交放在另外一個線程上進(jìn)行處理吧!于是,使用ThreadPool來改寫代碼:

static void Process()
{
    List batchItems = new List();
    foreach (var item in ...)
    {
        batchItems.Add(item);

        if (batchItems.Count > 1000)
        {
            ThreadPool.QueueUserWorkItem((o) =>
            {
                DataContext db = new DataContext();
                db.Items.InsertAllOnSubmit(batchItems);
                db.SubmitChanges();
            });                   

            batchItems = new List();
        }
    }
}

現(xiàn)在,我們將數(shù)據(jù)提交操作交給ThreadPoll執(zhí)行,當(dāng)線程池中有額外線程時,就會發(fā)起數(shù)據(jù)提交操作。而數(shù)據(jù)提交操作不會阻塞數(shù)據(jù)處理,因此按照那位兄弟的意圖,數(shù)據(jù)會不斷進(jìn)行處理,最后只要等待所有數(shù)據(jù)庫提交完成就可以了。思路很好,可惜運(yùn)行時發(fā)現(xiàn),原本(不利用多線程時)運(yùn)行正常的代碼,如今會“莫名其妙”地拋出異常。更為奇怪的是,數(shù)據(jù)庫中的數(shù)據(jù)出現(xiàn)了丟失的情況:處理了并“提交”了一百萬條數(shù)據(jù),但是數(shù)據(jù)庫里卻少了一部分。于是對著代碼左看右看,百思不得其解。

您看出問題原因來了嗎?

分析原因

要發(fā)現(xiàn)問題所在,我們必須了解匿名方法在.NET環(huán)境中的實(shí)現(xiàn)方式。

.NET中本沒有什么“匿名方法”,也沒有類似的新特性。“匿名方法”完全是由編譯器施展的魔法,它會將匿名方法中需要訪問的所有成員一起包含在閉包中,確保所有的成員調(diào)用都符合.NET標(biāo)準(zhǔn)。例如在文章第一節(jié)中的第2個示例,實(shí)際上由編譯器處理之后就變成了如下的樣子(自然字段名經(jīng)過“友好化”處理):

class TestClass
{
    ...

    private sealed class AutoGeneratedHelperClass
    {
        public TestClass m_testClassInstance;
        public int m_index;

        public void Action(string m)
        {
            this.m_index++;
            this.m_testClassInstance.Print(m);
        }
    }

    public void TestAfterCompiled()
    {
        AutoGeneratedHelperClass helper = new AutoGeneratedHelperClass();
        helper.m_testClassInstance = this;
        helper.m_index = 0;

        string[] messages = new string[] { "Hello", "World" };
        Action action = new Action(helper.Action);
        Array.ForEach(messages, action);

        Console.WriteLine(helper.m_index);
    }
}

由此就可以看出編譯器是如何實(shí)現(xiàn)一個閉包的:

編譯器自動生成一個私有的內(nèi)部輔助類,并將其設(shè)為sealed,這個類的實(shí)例將成為一個閉包對象。
如果匿名方法需要訪問方法的參數(shù)或局部變量,那么該參數(shù)或局部變量將“升級”成為輔助類中的公有Field字段。

如果匿名方法需要訪問類中的其它方法,那么輔助類中將保存類的當(dāng)前實(shí)例。
值得一提的是,在實(shí)際情況下以上三點(diǎn)理論都皆可能不滿足。在某些特別簡單的情況下(例如匿名方法中完全不涉及局部變量和其他方法),編譯器只會簡單生成一個靜態(tài)的方法來構(gòu)造一個委托實(shí)例,因?yàn)檫@樣可以獲得更好的性能。

對于之前的案例,我們現(xiàn)在也將它進(jìn)行一番改寫,這樣便可“避免”使用匿名對象,也可以清楚地展現(xiàn)出問題原因:

private class AutoGeneratedClass
{
    public List m_batchItems;

    public void WaitCallback(object o)
    {
        DataContext db = new DataContext();
        db.Items.InsertAllOnSubmit(this.m_batchItems);
        db.SubmitChanges();
    }
}

static void Process()
{
    var helper = new AutoGeneratedClass();
    helper.m_batchItems = new List();

    foreach (var item in ...)
    {
        helper.m_batchItems.Add(item);

        if (helper.m_batchItems.Count > 1000)
        {
            ThreadPool.QueueUserWorkItem(helper.WaitCallback);
            helper.m_batchItems = new List();
        }
    }
}

編譯器會自動生成一個AutoGeneratedClass類,并且在Process方法中使用這個類的實(shí)例來代替原來的batchItems局部變量。同樣,交給ThreadPool的委托對象也從匿名方法變成了AutoGeneratedClass實(shí)例的公有方法。因此線程池每次調(diào)用的便是該實(shí)例的WaitCallback方法。

現(xiàn)在問題應(yīng)該一目了然了吧?每次把委托交給線程池之后,線程池并不會立即執(zhí)行,而會保留到合適的時間再進(jìn)行。而WaitCallback方法在執(zhí)行時,它會讀取m_batchItems這個Field字段“當(dāng)前”所引用的對象。而與此同時,Process方法已經(jīng)“拋棄”了原本我們要提交的數(shù)據(jù),因此會引起提交到數(shù)據(jù)庫中數(shù)據(jù)的丟失。同時,在準(zhǔn)備每批次數(shù)據(jù)的過程中,很有可能會發(fā)起兩次數(shù)據(jù)提交,兩個線程提交同樣一批Item時,就拋出了所謂“莫名其妙”的異常。

解決問題

找到了問題所在,解決起來自然輕而易舉:

private class WrapperClass
{
    private List m_items;

    public WrapperClass(List items)
    {
        this.m_items = items;
    }

    public void WaitCallback(object o)
    {
        DataContext db = new DataContext();
        db.Items.InsertAllOnSubmit(this.m_items);
        db.SubmitChanges();
    }
}

static void Process()
{
    List batchItems = new List();
    foreach (var item in ...)
    {
        batchItems.Add(item);

        if (batchItems.Count > 1000)
        {
            ThreadPool.QueueUserWorkItem(
                new WrapperClass(batchItems).WaitCallback);

            batchItems = new List();
        }
    }
}

這里我們明確地準(zhǔn)備一個封裝類,用它來保留我們需要提交的數(shù)據(jù)。而每次提交時則使用保留好的數(shù)據(jù),自然不會發(fā)生不該有的“數(shù)據(jù)共享”,從而避免了錯誤的發(fā)生。

總結(jié)

匿名方法是強(qiáng)大的,但是也會造成一些令人難以察覺的陷阱。對于使用匿名方法創(chuàng)建的委托,如果不會立即同步執(zhí)行,并且其中使用了方法的局部變量,那么您就需要對其留個心眼了。因?yàn)榇藭r“局部變量”事實(shí)上已經(jīng)由編譯器轉(zhuǎn)變成一個自動類的實(shí)例上的Field字段,而這個字段將被當(dāng)前方法和委托對象共享。如果您在創(chuàng)建了委托對象之后還會修改共享的“局部變量”,那么請?jiān)偃_認(rèn)這樣做符合您的意圖,而不會造成問題。

此類問題也不光會出現(xiàn)在匿名方法中。如果您使用Lambda表達(dá)式創(chuàng)建了一個表達(dá)式樹,其中也用到了一個“局部變量”,那么表達(dá)式樹在解析或執(zhí)行時同樣也會獲取“當(dāng)前”的值,而不是創(chuàng)建表達(dá)式樹時的值。

這也是為什么Java中的內(nèi)聯(lián)寫法——匿名類——如果要共享方法內(nèi)的“局部變量”,則必須將變量使用final關(guān)鍵字來修飾:這樣這個變量只能在聲明時賦值,避免了后續(xù)的“修改”可能會造成的“古怪問題”。

【編輯推薦】

  1. .NET動靜結(jié)合編程 接口和委托的約束強(qiáng)度
  2. 使用.NET Array類的Sort方法分類數(shù)值
  3. 詳論在.NET中定義結(jié)構(gòu)設(shè)計(jì)標(biāo)準(zhǔn)
責(zé)任編輯:彭凡 來源: 博客園
相關(guān)推薦

2024-06-12 08:21:07

Deadlock死鎖版本

2010-01-05 15:43:13

.NET Framew

2010-01-12 18:28:28

VB.NET共享變量

2009-11-03 10:51:33

VB.NET共享

2011-05-20 16:34:35

VB.NET

2010-01-18 14:54:00

VB.NET共享成員變

2009-11-03 11:40:37

VB.NET共享變量

2009-09-21 08:50:42

.NET中文變量

2010-01-21 16:37:56

VB.NET變量聲明

2009-08-05 17:11:38

匿名方法的作用

2010-12-16 13:56:57

匿名對象.NET

2023-11-27 16:20:25

2009-07-23 16:21:07

static變量ASP.NET

2024-11-12 07:28:39

2009-08-20 16:28:45

C#匿名方法

2009-08-20 16:15:19

C# 匿名方法

2022-02-24 23:59:05

人工智能下棋隱私

2020-11-03 06:57:10

MyBatis數(shù)據(jù)庫

2019-09-18 15:20:16

MyBatisSQL數(shù)據(jù)庫

2011-06-08 11:05:38

getpost
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

一本色道久久综合| 黄色小视频大全| 日韩精品一区在线视频| 中文在线观看av| 国产无遮挡裸体免费久久| 蜜桃视频在线观看网站| www黄色在线观看| 欧美三级伦理在线| 欧美日韩亚洲精品一区二区三区| 久久精品久久久久久| 国产成人a亚洲精v品无码| 性生交生活影碟片| 亚洲情侣在线| 91精品久久久久久蜜臀| 午夜精品美女久久久久av福利| 国产无精乱码一区二区三区| 日本亚州欧洲精品不卡| 国产精品久久久久一区二区三区 | 亚洲av成人精品一区二区三区| 丁香在线视频| 日日骚欧美日韩| 亚洲欧洲xxxx| 免费观看成人在线视频| 免费在线稳定资源站| 国产一区在线观看视频| 精品国产一区久久久| 中文字幕 亚洲一区| 日本三级一区| 久久午夜羞羞影院免费观看| 91高清视频免费观看| 91精品小视频| 日韩中文字幕视频网| 欧美日韩在线观看一区二区| 中文字幕人成一区| 99久久免费国产精精品| 欧美理论在线| 337p日本欧洲亚洲大胆精品| 国产v片免费观看| 日韩大片b站免费观看直播| 久久青草久久| 色婷婷久久av| 午夜免费视频网站| 欧美14一18处毛片| 91女厕偷拍女厕偷拍高清| 国产91热爆ts人妖在线| 卡一卡二卡三在线观看| 无遮挡爽大片在线观看视频 | 黄色成人在线观看| 狠狠狠色丁香婷婷综合激情 | 久久中文免费视频| 日韩视频在线直播| 欧美精品在线一区二区三区| 亚洲色婷婷久久精品av蜜桃| 日批免费在线观看| 模特精品在线| 久久精品国产一区| 亚洲欧洲综合网| 福利在线一区| 精品sm在线观看| 精品人妻在线视频| 日韩高清中文字幕一区二区| 亚洲视频免费在线| 明星裸体视频一区二区| 99久久久久成人国产免费| 国产麻豆成人传媒免费观看| 97婷婷涩涩精品一区| 污污视频网站在线免费观看| 亚洲专区**| 色综合久久中文综合久久97 | 欧美日韩午夜| 久久人人爽国产| 美女100%无挡| 91午夜精品| 欧美日韩免费观看一区二区三区| 91免费黄视频| 色戒汤唯在线观看| 欧洲另类一二三四区| 女人色极品影院| 日本a级在线| 久久久三级国产网站| 99热最新在线| 国产精品主播一区二区| 羞羞视频在线观看欧美| 欧美风情在线观看| 免费精品在线视频| 欧美男同视频网| 亚洲国产精品va在线看黑人动漫| 日韩av影视大全| 99riav视频一区二区| 精品久久久久久中文字幕| 警花观音坐莲激情销魂小说| www.av在线| 久久蜜桃香蕉精品一区二区三区| 午夜精品亚洲一区二区三区嫩草| 国产黄a三级三级三级av在线看 | 欧美一区二区三区视频在线| 免费日韩中文字幕| 久热在线观看视频| 欧美影片第一页| 亚洲成熟丰满熟妇高潮xxxxx| 在线一区视频观看| 在线欧美一区二区| 欧美熟妇另类久久久久久多毛| 成人国产一区二区三区精品麻豆| 欧美一区二区精品在线| 精品国产无码在线观看| 欧美成人高清| 久久成人亚洲精品| 日韩精品一区二区亚洲av性色| 日韩理论电影大全| 色诱女教师一区二区三区| 国产一级二级三级视频| 国内精品福利| 国模极品一区二区三区| 波多野结衣黄色网址| 日韩精品一二三区| 成人在线看片| 女人18毛片一区二区三区| 丁香婷婷综合五月| 精品伦理一区二区三区| 日本一区高清| 亚洲自拍偷拍欧美| 欧美久久久久久久久久久久久| 国产精品一区二区免费福利视频 | 国产亲伦免费视频播放| 久久久久国产精品人| 国产无限制自拍| 欧美专区视频| 久久精品视频一| 中文字字幕在线观看| 26uuu精品一区二区三区四区在线| 欧美交换配乱吟粗大25p| 久久婷婷五月综合色丁香| 亚洲色图校园春色| 国产精品久久久久久久久久久久久久久久久 | 久久国产精品美女| 日韩免费看网站| 久久久久成人精品无码中文字幕| 国产精品99视频| 欧美精品久久久久a| 日韩成人免费在线观看| 免费亚洲一区| 精品乱码一区二区三区| 国模私拍视频在线播放| 日韩欧美国产黄色| 日韩中文字幕a| 伊人www22综合色| 蜜臀久久99精品久久久无需会员| 日本熟女一区二区| 国产成人一级电影| 欧美日韩天天操| 快射av在线播放一区| 欧美亚洲精品一区| 国产欧美一区二区三区在线观看视频| 丝袜美腿亚洲综合| 日韩精品大片| 超碰在线免费公开| 91精品国产综合久久久蜜臀粉嫩| 精品一区二区在线观看视频| 久久精品国产亚洲一区二区三区| 国产激情美女久久久久久吹潮| 2021国产在线| 一本一道久久a久久精品综合蜜臀| 日本一级片在线播放| 亚洲女同在线| 日韩视频精品| www.欧美视频| 亚洲色图25p| 真实新婚偷拍xxxxx| 国产精品国产自产拍高清av王其| 99在线精品免费视频| 精品精品国产三级a∨在线| 日韩性生活视频| 最新中文字幕一区| 国产欧美久久久精品影院| 欧美又粗又长又爽做受| 亚洲精品国产动漫| 欧美国产极速在线| 五十路在线观看| 亚洲女与黑人做爰| 国产精品一区二区小说| 久久久久久美女精品| 国产99在线|中文| 在线观看黄av| 欧洲精品中文字幕| 国产这里有精品| 久久国产精品区| 日韩av图片| 白嫩亚洲一区二区三区| 午夜精品一区二区三区在线视频| 精华区一区二区三区| 岛国av一区二区| 国产中文字幕久久| 成人激情黄色小说| 欧美久久在线观看| 日韩中文字幕在线一区| 欧美亚洲国产日本| 天堂资源最新在线| 精品女同一区二区三区在线播放 | 香蕉久久精品| 久久久久久久久久久免费 | 久久高清视频免费| 日韩精品系列| 欧美成人三级在线| 青春草免费视频| 国产三级一区二区| 国产第一页视频| 欧美日韩国产精品一区二区亚洲| 日本精品一区| 69堂精品视频在线播放| 欧美国产乱视频| 欧美videos极品另类| 亚洲欧美一区二区三区四区| 久久夜色精品国产噜噜亚洲av| 26uuu成人网一区二区三区| 日本精品一区在线| 日本免费新一区视频| 亚洲国产精品一区二区第一页| 日本一道高清亚洲日美韩| 欧美区在线播放| 人妻无码中文字幕| 欧美放荡的少妇| 夜夜爽妓女8888视频免费观看| 亚洲电影一区二区| 久久国产精品无码一级毛片| 国产精品美女| 日本在线免费观看一区| 三级欧美日韩| 成人激情视频在线播放| 最爽无遮挡行房视频在线| 国产亚洲精品一区二555| 亚洲一级片免费看| 亚洲激情自拍视频| 一级黄色大片免费看| 国产一区白浆| 日韩欧美猛交xxxxx无码| 久久精品亚洲人成影院| 一区二区免费电影| 6080亚洲理论片在线观看| 国产在线98福利播放视频| 牛牛电影国产一区二区| 久久久国产91| 国产最新在线| 免费不卡欧美自拍视频| 成年人黄视频在线观看| 欧美成人午夜激情视频| 性xxxxfjsxxxxx欧美| 日韩av一区二区在线| 美女黄页在线观看| 亚洲国产一区在线观看| 国产精品国产三级国产专业不| 91原创在线视频| 中文字幕国产高清| 欧美资源在线| 日本老熟妇毛茸茸| 青青草国产精品97视觉盛宴| 欧美婷婷精品激情| 欧美激情四色| 久久人妻无码一区二区| 亚洲大胆视频| 午夜精品一区二区在线观看| 精品日韩免费| 一区二区不卡在线观看| 亚洲欧美在线专区| 亚洲理论电影在线观看| 国产欧美不卡| 国产免费999| 亚洲深夜激情| 无码人妻精品一区二区三区66| 麻豆一区二区三| 黑人无套内谢中国美女| 97久久精品人人爽人人爽蜜臀| 亚洲黄色小说视频| 不卡av免费在线观看| av网站有哪些| 中文无字幕一区二区三区| 91玉足脚交白嫩脚丫| 久久久久久久网| 99鲁鲁精品一区二区三区| 亚洲一区二区三区在线看| 日韩欧美在线观看免费| 欧美日本国产一区| 免费观看黄色av| 国产亚洲精品一区二区| 深夜国产在线播放| 国产福利视频一区二区| 在线女人免费视频| 国产一区视频在线| 久久免费视频66| 亚洲一区二区三区午夜| 亚洲国产精品第一区二区| a级网站在线观看| 99成人免费视频| 在线观看免费不卡av| 免费高清成人在线| 欧美一级裸体视频| 国产电影一区在线| av电影在线不卡| 亚洲午夜一二三区视频| 中文永久免费观看| 亚洲精品国产综合区久久久久久久| av 一区二区三区| 欧美一区二区福利在线| 青草久久伊人| 亚洲一区 在线播放| 日韩中文视频| 成人欧美一区二区三区视频 | 亚洲国产私拍精品国模在线观看| 懂色av中文在线| 91爱视频在线| 日本一区影院| 亚洲欧洲精品一区二区三区波多野1战4| 一区免费视频| 成人免费黄色av| 国产精品乱人伦| 一级片免费在线播放| 一区二区在线观看免费视频播放| 激情视频网站在线观看| 精品国产乱码久久久久久老虎 | 亚洲天堂免费在线观看视频| 欧美特黄aaaaaa| 亚洲成年网站在线观看| 在线黄色网页| 成人淫片在线看| 久久亚洲国产| 99精品一区二区三区的区别| 视频一区二区中文字幕| 国产ts丝袜人妖系列视频| 亚洲狠狠爱一区二区三区| av网站在线观看免费| 日韩亚洲成人av在线| 欧美91在线|欧美| 天堂资源在线亚洲资源| 久久久久久亚洲精品杨幂换脸| 亚洲国产精品成人综合久久久| 午夜精品成人在线| 四虎影院在线免费播放| 日韩精品高清在线观看| av蜜臀在线| 国产精品极品美女在线观看免费 | 午夜小视频福利在线观看| 91精品国产自产在线老师啪| 久久免费精品视频在这里| 在线观看高清免费视频| 国产精品久久久久久久午夜片 | 精品亚洲欧美一区| chinese全程对白| 在线成人av网站| 成人av免费| 国产精华一区| 亚洲黄色天堂| 蜜桃精品成人影片| 色狠狠综合天天综合综合| 一级黄色录像大片| 日韩精品一区在线| 啪啪免费视频一区| 国产精品久久久久久久久久直播 | 一区二区三区动漫| 欧美卡一卡二| 国产九色精品| 日韩午夜电影| 欧美黄色一级生活片| 欧美日韩精品三区| 在线播放免费av| 精品一卡二卡三卡四卡日本乱码 | 精品国产乱码久久久久久天美| 黄色美女一级片| 深夜福利日韩在线看| 亚洲欧美久久精品| www.日本三级| 91在线观看地址| 中文在线a天堂| 欧美激情高清视频| 尤物tv在线精品| 亚欧激情乱码久久久久久久久| 亚洲精品久久久蜜桃| 神马亚洲视频| 成人福利视频网| 99精品国产在热久久下载| 亚洲精品色午夜无码专区日韩| 4438x成人网最大色成网站| 国产蜜臀在线| 亚洲v国产v| 国产·精品毛片| 国产67194| 亚洲国产福利在线| 91大神在线观看线路一区| 国产日韩第一页| 91女神在线视频| 国产99对白在线播放| 欧美中在线观看| 青青草久久爱| 青青草精品视频在线| 国产日韩欧美一区二区三区乱码| 国产欧美综合视频| 热久久这里只有精品| 欧美精品日本| 国产在线免费av| 日韩激情第一页| 日本少妇精品亚洲第一区|