青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

cexer

cexer
posts - 12, comments - 334, trackbacks - 0, articles - 0
  C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

轉帖請注明出處 http://m.shnenglu.com/cexer/archive/2008/07/08/55670.html

  單件(Singelton)模式可以說是眾多設計模式當中,理解起來最容易,概念最為簡單的一個。并且在實際的設計當中也是使用得又最為頻繁的,甚至有很多其它的模式都要借助單件才能更好地實現。然而就是這樣被強烈需求的“一句話模式”(一句話就能闡述明白),雖然有無數的牛人浸淫其中,至今也沒有誰鼓搗出一個完美的實現。我小菜鳥一只自然更不敢逢人便談單件。不過這個貼的主題是跟單件模式是密不可分的。

  什么又叫做“線程相關的單件模式”呢?也許你已經顧名思義猜出了八九分。不過還是允許我簡單地用實例說明一下。

  假設你要設計了一個簡單的 GUI 框架,這個框架當中需要這樣一個全局變量(單件模式),它保存了所有窗口句柄與窗口指針的映射(我見過的數個的開源 GUI 框架都有類似的東西。)。在 WIN32 平臺上就是這樣一個簡單的東西:

    //窗口的包裝類
class Window
{
HWND m_hwnd;
public:
bool create();
bool destroy();

//其它細節
};

//窗口句柄與其對象指針的映射
typedef map<HWND,Window*> WindowMap;
typedef WindowMap::iterator WindowIter;
WindowMap theWindowMap;




  每創建一個窗口,就需要往這個 theWindowMap 當中添加映射。每銷毀一個窗口,則需要從其中刪除掉相關映射。實現代碼類似:

    //創建窗口
bool Window::create()
{
m_hwnd=::CreateWindow(/*參數略*/);
if(!::IsWindow(m_hwnd))
return false;

theWindowMap[m_hwnd]=this; //添加映射
return true;
}

//銷毀窗口
bool Window::destroy()
{
::DestroyWindow(m_hwnd);

theWindowMap.erase(m_hwnd); //刪除映射
return true;
}


  你可以用任何可能的單件模式來實現這樣一個全局變量 theWindowMap,它會 工作得很好。但是當如果考慮要給程序添加多線程支持(“多線程”是如此麻煩,它總愛和“但是”一起出現,給本來進行得很順利的事情引起波折。),就會發現此時也許純粹的單件模式并不是最好的選擇。例如一個線程同時創建窗口,那么兩個線程同時調用:

    theWindowMap[m_hwnd]=this;


  這顯然不是一個原子操作,可以肯定如果你堅持這樣干你的程序會慢慢走向崩潰,幸運一點只是程序運行結果錯誤,如果你恰好那幾天印堂發暗面色發灰,說不定就因為這小小的錯誤,被無良的BOSS作為借口開除掉了,那可是個悲慘的結局。

  當然大多數的單件模式已經考慮到了多線程的問題。它們的解決方案就是給加上線程鎖 ,我在數個開源的 GUI 框架看到他們都采用了這種解決方案。不過這樣做,在線程同步過程當中,會產生與 GUI 框架邏輯不相關的同步消耗,雖然不是什么大不了的消耗,但是客戶可能因此就選擇了你的竟爭對手,如果線程竟爭激烈,在強烈渴求資源的環境(如小型移動設置)當中,這種消耗更是不可忽視的。

  實際上在應用當中,極少有線程需要插入刪除其它線程創建的窗口映射(如果確實有這種需要,那么可以肯定項目的設計上出了問題)。在這種情況下本線程創建窗口映射都將只是本線程存取,類似“Thread-Specific”的概念。也就是說,theWindowMap 當中其它線程創建的窗口的映射對于本線程來說都是不需關心的,我們卻要為那部分不必要東西整天提心吊膽并付出運行時消耗的代價,這也有點像“穿著棉襖洗澡”。但是怎么樣才能做到更輕松爽快些呢?

  就本例問題而言,我們需要這樣一種變量來保存窗口映射,它針對每個線程有不同的值(Thread-Specific Data),這些值互不影響,并且所有線程對它的訪問如同是在訪問一個進程內的全局變量(Singelton)。

  如果你是熟悉多線程編程的人,那么“Thread-Specific ”一定讓你想起了什么。是的,“Thread-Specific Storage ” (線程相關存存諸,簡稱 TSS ),正是我們需要的,這是大多數操作系統都提供了的一種線程公共資源安全機制,這種機制允許以一定方式創建一個變量,這個變量在所在進程當中的每個線程當中,可以擁有不同的值。在 WIN32 上,這個變量就稱為“索引”,其相關的值則稱為“槽”, “Thread-Local Storage”(線程局部存諸,簡稱 TLS )機制。它的提了供這樣幾個函數來定義,設置,讀取線程相關數據(關于 TLS 的更多信息,可以查閱 MSDN ):

    //申請一個“槽”的索引。
DWORD TlsAlloc( void );

//獲得調用線程當中指定“槽”的值。
VOID* TlsGetValue( DWORD dwTlsIndex );

//設置調用線程當中指定“槽”的值。
BOOL TlsSetValue( DWORD dwTlsIndex,VOID* lpTlsValue );

//釋放掉申請的“槽”的索引
BOOL TlsFree( DWORD dwTlsIndex );

  具體使用流程方法:先調用 TlsAlloc 申請一個“索引”,然后線程在適當時機創建一個對象并調用 TlsSetValue 將“索引”對應的“槽”設置為該對象的指針,在此之后即可用 TlsGetValue 訪問該“糟”。最后在不需要的時候調用 TlsFree ,如在本例當中,調用 TlsFree 的最佳時機是在進程結束時。

  先封裝一下 TlsAlloc 和 TlsFree  以方便對 ”索引“的管理。

    class TlsIndex
{
public:
TlsIndex()
:m_index(::TlsAlloc())
{}

~TlsIndex()
{
::TlsFree(m_index);
}

public:
operator DWORD() const
{
return m_index;
}

private:
DWORD m_index;
};

  
  如你所見,類 TlsIndex 將在構造的時候申請一個“索引”,在析構的時候釋放此“索引”。

  在本例當中 TlsIndex 的對象應該存在進程的生命周內,以保證在進程退出之前,這個“索引”都不會被釋放,這樣的 TlsIndex 對象聽起來正像一個全局靜態對象,不過 Meyers Singelton (用函數內的靜態對象實現)在這里會更適合,因為我們不需要對這個對象的生命周末進行精確控制,只需要它在需要的時候創建,然后在進程結束前銷毀即可。這種方式只需要很少的代碼即可實現,比如:

    DWORD windowMapTlsIndex()
{
static TlsIndex s_ti;  //提供自動管理生命周期的“索引”
return s_ti;
}


  利用這個“索引”,我們就能實現上述“Thread-Specific”的功能:

    WindowMap* windowMap()
{
WindowMap* wp=reinterpret_cast<WindowMap*>(::TlsGetValue(windowMapTlsIndex()));
if(!wp)
{
wp=new WindowMap();
::TlsSetValue(windowMapTlsIndex(),wp);
}
return wp;
}

#define theWindowMap *(windowMap())

  
  注意各線程訪問以上的代碼不會存在竟爭。這樣就實現了一個線程安全且無線程同步消耗版本的“全局對象” theWindowMap 。我們甚至不用改變Window::create,Window::destory,queryWindow 的代碼,

  這幾個簡單的函數看起來似乎不像一個“模式”,但是它確實是的。

  現在總結一下“線程相關的單件模式”的概念:保證一個類在一個線程當中只有一個實例,并提供一個訪問它的線程內的訪問點的模式。

  為了不重復地制造車輪,我將此類應用的模式封裝了一下:

    template<typename TDerived>
class TlsSingelton
{
typedef TDerived _Derived;
typedef TlsSingelton<TDerived> _Base;

public:
static _Derived* tlsInstance()
{
return tlsCreate();
}

protected:
static _Derived* tlsCreate()
{
_Derived* derived=tlsGet();
if(derived)
return derived;

derived=new _Derived();
if(derived && TRUE==::TlsSetValue(tlsIndex(),derived))
return derived;

if(derived)
delete derived;

return NULL;
}

static bool tlsDestroy()
{
_Derived* derived=tlsGet();
if(!derived)
return false;

delete derived;
return true;
}

static DWORD tlsIndex()
{
static TlsIndex s_tlsIndex;
return s_tlsIndex;
}

private:
static _Derived* tlsGet()
{
return reinterpret_cast<_Derived*>(::TlsGetValue(tlsIndex()));
}

static bool tlsSet(_Derived* derived)
{
return TRUE==::TlsSetValue(tlsIndex(),derived);
}

//noncopyable
private:
TlsSingelton(const _Base&);
TlsSingelton& operator=(const _Base&);
};


  將 tlsCreate,tlsDestroy 兩個函數設置為保護成員,是為了防止一些不三不四吊爾啷噹的程序隨意地刪除。

  示例:

    class WindowMapImpl:public TlsSingelton<WindowMap>
{
WindowMap m_map;
public:
WidnowMap& theWindowMapImpl()
{
return m_map;
}

public:
~WindowMapImpl();

protected:
WindowMapImpl(); //只能通過tlsCreate創建
friend class _Base;
};

#define theWindowMap (WindowMapImpl::tlsInstance()->theWindowMapImpl())



  仍不需要修改原有窗口代碼。

Feedback

# re: 線程相關的單件模式(Thread-Specific Singelton)  回復  更多評論   

2008-07-09 16:57 by www.helpsoff.com.cn
哈哈,有意思!看到一半的時候,心想有必要引入tls嗎,只要設好編譯選項,map應該是線程安全的呀;不過看到后面,把tls引入然后封裝到原有實現上去,覺得很精彩,其實已經跳出了討論所謂singleton和線程安全的范疇了,雖然目的是這個。

# re: 線程相關的單件模式(Thread-Specific Singelton)  回復  更多評論   

2008-07-09 18:32 by cexer
呵呵謝謝耐心看完。map可以通過編譯器設置線程安全?我還不知道呢。不過引入tls與“線程相關的單件”,是為了可以讓更多更復雜的此類應用更容易實現。還有map的線程安全是由實現提供的(非標準),應該不是所有編譯器都支持的吧?

# re: 線程相關的單件模式(Thread-Specific Singelton)  回復  更多評論   

2008-07-10 01:44 by www.helpsoff.com.cn
我大致記得用cl編譯鏈接生成binary的時候帶"/MT"就是連接多線程庫,不過印象不深了,博主有興趣可以看看。

我同意博主的說法,引入tls確實是為實現類似應用做好了封裝。

# re: 線程相關的單件模式(Thread-Specific Singelton)  回復  更多評論   

2008-07-10 09:02 by cexer
@www.helpsoff.com.cn
這個"/MT"應該指的鏈接是微軟運行時庫,C運行時庫。C++標準庫對包括map在內的容器在多線程環境當中的情況沒有采取任何的保護措施。

# re: 線程相關的單件模式(Thread-Specific Singelton)  回復  更多評論   

2008-07-10 14:09 by www.helpsoff.com.cn
對頭,/MT是指連接微軟的runtime lib,相對于標準庫來將,這個庫的實現是支持多線程的。不過博主說的對,連接這個庫并不能保證map是線程安全了。獻丑了...

# re: 線程相關的單件模式(Thread-Specific Singelton)  回復  更多評論   

2008-07-10 22:53 by 夢在天涯
TlsObject<***> 這個東東哪里來的那,TLS倒是蠻好用的哦!

http://m.shnenglu.com/mzty/archive/2007/08/01/28892.html

# re: 線程相關的單件模式(Thread-Specific Singelton)  回復  更多評論   

2008-07-10 23:22 by cexer
@夢在天涯
應該是TlsSingelton,我寫錯了哈,謝謝提醒。

# re: 線程相關的單件模式(Thread-Specific Singelton)  回復  更多評論   

2009-08-31 22:42 by stidio
這樣寫,用處不大,其實線程局部化本身用處就不大
如果這樣寫,同一個線程中持有的是不同的映射實例,而很多情況(不僅僅是窗口映射),這樣做的目的,是為了根據一個index獲得一個結果,也就是查詢;
如果這樣最,對于跨線程查詢,你必須破壞你的設計;

不知道是不是我理解的問題,單件模式的引出,是為了確定資源的唯一性;而你的這個恰恰不是;例如:
張三,李四的老板是王五,那王五對于張三,李四來說,是他的"單件"
張7,張8的爸爸是張9,那張9是單件

而你卻構建了一個,張9和王五的集合,說這是另外4個人的單件,這并不符合唯一性條件;

關于單件模型的多線程問題,其實單件本身沒多線程問題,多線程問題的引入是在對單件對象的使用上;如果說單件存在著多線程問題,那也僅僅需要在創建時鎖定(比如說2個線程同時獲得,都為空,創建2次;這樣需要在創建時鎖定,并做二次判斷,如
if(xx == 0) {
lock();
if(xx == 0)
xx = new XX;
....
}
而其實大多數情況不需要這樣來折騰;


超哥不錯哈,出去后的確進步了很多;

# re: 線程相關的單件模式(Thread-Specific Singelton)  回復  更多評論   

2009-09-01 12:15 by cexer
@stidio
這個設計是設計給不跨線程的應用的,主要考慮在這種應用下,如果線程太多,都去查詢同一個全局的東西,大多數線程都一直處于等待資源的狀態,比較浪費CPU時間。
這是在公司時寫的哈,出來后倒沒寫了,感覺是人越來越懶了,寫程序越來越沒激情

# re: 線程相關的單件模式(Thread-Specific Singelton)  回復  更多評論   

2010-05-12 16:10 by ZeroQ
#define theWindowMap (WindowMapImpl::tlsInstance()->theWindowMapImpl())
上面一行中,WindowMapImpl::tlsInstance()返回的是WindowMap實例,而WindowMap并沒有theWindowMapImpl()方法,不知道這樣是如何實現的。請教嘍。。。

只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲剧情一区二区| 欧美亚洲在线播放| 欧美中文字幕在线| 亚洲一区二区毛片| 亚洲男人的天堂在线观看| 亚洲图中文字幕| 在线午夜精品| 午夜精品久久久久久久久久久久 | 夜夜嗨av色综合久久久综合网| 91久久视频| 亚洲精品国产精品国自产观看浪潮| 亚洲国产欧美一区二区三区久久| 亚洲激情偷拍| 亚洲欧美美女| 久久裸体艺术| 欧美.日韩.国产.一区.二区| 欧美成人tv| 一本不卡影院| 久久精品国产清高在天天线 | 亚洲第一天堂av| 久久免费精品视频| 欧美激情一区二区| 亚洲午夜日本在线观看| 欧美中文字幕视频在线观看| 欧美顶级少妇做爰| 国产精品一区二区黑丝| 亚洲欧洲美洲综合色网| 午夜精品一区二区三区在线视| 久热国产精品| 99这里有精品| 免费不卡在线观看| 国产婷婷色一区二区三区在线| 亚洲国产精品v| 久久国产精品久久精品国产| 亚洲精品午夜精品| 久久精品国产第一区二区三区最新章节 | 蜜桃av一区二区在线观看| 国产精品免费视频xxxx| 亚洲精品极品| 久久久午夜电影| 亚洲午夜视频在线| 欧美日韩亚洲一区二区三区| 亚洲高清资源综合久久精品| 久久国产精品99久久久久久老狼| 欧美成人精品三级在线观看| 亚洲人成网站777色婷婷| 在线综合亚洲欧美在线视频| 欧美fxxxxxx另类| 韩国av一区二区三区四区| 亚洲一区二区四区| 亚洲国产日韩欧美一区二区三区| 久久精品视频在线免费观看| 国产精品一区二区你懂得| 一本色道88久久加勒比精品| 亚洲第一在线视频| 久久视频一区二区| 精品动漫3d一区二区三区| 久久国产88| 欧美一级视频精品观看| 欧美性猛交一区二区三区精品| 亚洲美女中出| 亚洲国产成人porn| 麻豆国产va免费精品高清在线| 国产一区二区三区奇米久涩 | 久久影院午夜片一区| 亚洲欧美成人网| 国产精品久久久爽爽爽麻豆色哟哟 | 久久婷婷国产综合尤物精品| 性欧美videos另类喷潮| 国产一区二区三区视频在线观看| 欧美淫片网站| 久久国产欧美精品| 亚洲高清视频在线观看| 亚洲国内自拍| 欧美日韩在线影院| 亚洲欧美久久久久一区二区三区| 亚洲一区二区在线播放| 国产一区二区三区成人欧美日韩在线观看 | 国产精品久久久一区二区三区| 亚洲欧美日韩综合| 性色av一区二区三区红粉影视| 狠狠色2019综合网| 亚洲激情一区二区| 国产精品久久久久一区二区| 久久久精品欧美丰满| 亚洲人成绝费网站色www| 99精品视频一区| 黄色亚洲精品| 夜夜嗨av一区二区三区中文字幕| 国产精品久久国产三级国电话系列| 欧美成人精品一区二区| 亚洲国产精品小视频| 久久不见久久见免费视频1| 妖精成人www高清在线观看| 久久国产精品电影| 欧美影视一区| 亚洲观看高清完整版在线观看| 国产精品99久久久久久www| 亚洲免费观看高清在线观看 | 久久免费99精品久久久久久| 夜夜爽www精品| 欧美日韩18| 欧美成人免费va影院高清| 在线欧美日韩精品| 免费观看在线综合| 久久精品视频在线看| 亚洲精品影视在线观看| 午夜精品久久久久久久99热浪潮| 在线视频你懂得一区| 欧美性大战久久久久久久| 亚洲高清自拍| 亚洲精品欧洲| 久久久91精品国产一区二区三区 | 亚洲精品美女| 国产精品久久久久久妇女6080| 另类欧美日韩国产在线| 欧美性事在线| 亚洲激情在线| 伊人婷婷久久| 老司机精品视频一区二区三区| aa成人免费视频| 老司机凹凸av亚洲导航| 妖精成人www高清在线观看| 国产精品乱子久久久久| 久久精品在线免费观看| 国内精品久久久久久久影视麻豆 | 欧美精品导航| 亚洲第一在线视频| 午夜激情综合网| 激情欧美一区| 国产精品福利网| 欧美电影免费观看高清| 久久精品99久久香蕉国产色戒| 亚洲精品一区久久久久久 | 国产综合激情| 久久综合伊人77777麻豆| 女人香蕉久久**毛片精品| 一本久久a久久精品亚洲| 国内久久精品| 欧美激情一区二区久久久| 欧美国产精品一区| 亚洲国产免费看| 欧美日韩精品中文字幕| 国产午夜久久久久| 午夜亚洲影视| 亚洲免费观看高清在线观看| 久久久久久久性| 欧美中文字幕视频| 国产一区在线视频| 午夜激情综合网| 久久久久久久久久久一区 | 中文在线资源观看网站视频免费不卡| 久久久www| 欧美专区日韩专区| 欧美一区深夜视频| 欧美理论片在线观看| 欧美另类亚洲| 国产在线一区二区三区四区| 国产精品毛片va一区二区三区 | 久久午夜视频| 性欧美超级视频| 久久精品国产成人| 你懂的一区二区| 亚洲小说欧美另类婷婷| 欧美一级电影久久| 欧美电影免费观看高清| 国产欧美日韩综合精品二区| 精品999在线播放| 午夜久久福利| 亚洲精品在线三区| 久久久午夜视频| 国产精品午夜春色av| 99精品国产高清一区二区| 久久午夜视频| 免费一区二区三区| 国产日韩精品一区观看| 在线观看中文字幕不卡| 99热这里只有精品8| 美腿丝袜亚洲色图| 欧美电影资源| 香港成人在线视频| 久久精品综合一区| 亚洲黄色影片| 国产亚洲女人久久久久毛片| 性欧美办公室18xxxxhd| 在线观看日韩av先锋影音电影院| 欧美大片免费| 欧美在线你懂的| 99精品久久久| 免费视频最近日韩| 欧美一区二区三区四区视频| 亚洲国产mv| 国产美女一区| 欧美日韩一区二区三区| 美女精品视频一区| 久久先锋资源| 欧美日韩国产电影| 久久精品最新地址| 亚洲网站在线看| 久久大综合网|