淺談更糟糕的 CS_CLASSDC 標(biāo)志位的作用
在上一篇文章中,我們了解了 CS_OWNDC 標(biāo)志位的歷史,也說(shuō)明了設(shè)計(jì)它的初衷。
這個(gè)標(biāo)志位一開始看起來(lái)是個(gè)挺好的設(shè)計(jì),但是如果你多琢磨一會(huì)兒,就會(huì)發(fā)現(xiàn)它不是一個(gè)好主意。今天我們來(lái)看看更糟的。
CS_CLASSDC 標(biāo)志位有點(diǎn)類似 CS_OWNDC ,但更糟糕的是,它把 CS_OWNDC 的所有問(wèn)題都放大了。此話怎講?
我們先回想一下,CS_OWNDC 標(biāo)志指示窗口管理器為窗口創(chuàng)建 DC,并使用該單個(gè) DC 來(lái)響應(yīng)對(duì) BeginPaint 和 GetDC 的調(diào)用。CS_CLASSDC 更進(jìn)一步,為該類的所有窗口創(chuàng)建一個(gè) DC。因此,我上次使用一個(gè)函數(shù)顯示的問(wèn)題,該函數(shù)認(rèn)為它有一個(gè)窗口有兩個(gè)不同的 DC,現(xiàn)在甚至可以跨窗口發(fā)聲。你認(rèn)為一個(gè)窗口有一個(gè) DC,另一個(gè)窗口有另一個(gè) DC,但實(shí)際上它們是相同的!
更糟糕的是,兩個(gè)線程可以同時(shí)使用相同的 DC。GDI 中沒(méi)有任何禁止它的內(nèi)容;這只是一場(chǎng)競(jìng)賽,看看哪個(gè)線程的變化占上風(fēng):”最后一個(gè)界面繪制代碼將會(huì)獲勝”。
假設(shè)兩個(gè)線程碰巧每個(gè)線程都有一個(gè)來(lái)自同一窗口類的 CS_CLASSDC 窗口,并假設(shè)兩個(gè)窗口都需要重新繪制。每個(gè)窗口都會(huì)收到一條 WM_PAINT 消息,兩個(gè)線程都進(jìn)入其繪制代碼。
但這些線程不知道的是它們?cè)谕粋€(gè) DC 上運(yùn)行。
在線程 A 中運(yùn)行的代碼完全期望文本為紅色,因?yàn)樗鼘⑽谋绢伾O(shè)置為紅色,然后繪制文本。怎么知道就在那一刻,線程 B 去把它改成了藍(lán)色?
這是一種競(jìng)爭(zhēng)條件錯(cuò)誤,你可能永遠(yuǎn)無(wú)法在受控條件下研究。你只會(huì)收到來(lái)自客戶的錯(cuò)誤報(bào)告,說(shuō)也許每個(gè)月一次,一個(gè)項(xiàng)目的顏色錯(cuò)誤,也許你自己偶爾會(huì)看到它,但當(dāng)你設(shè)置了調(diào)試器斷點(diǎn)時(shí),它永遠(yuǎn)不會(huì)發(fā)生。即使添加其他診斷代碼,也只會(huì)看到以下內(nèi)容:
太好了,斷言被觸發(fā)了。你剛剛設(shè)置的顏色不存在?,F(xiàn)在你要做什么?也許你只會(huì)說(shuō)“這不會(huì)是操作系統(tǒng)出了Bug吧?”并將你的代碼更改為下圖的代碼:
即使這樣也不能解決問(wèn)題,因?yàn)榫€程 B 可能在 GetTextColor 和調(diào)用 DrawText 后將顏色更改為藍(lán)色?,F(xiàn)在,每六個(gè)月只有一次,繪制的的顏色是錯(cuò)誤的。
你向 Microsoft 發(fā)誓 ,發(fā)誓從現(xiàn)在開始開發(fā) MAC 平臺(tái)上的軟件。
好的,所以現(xiàn)在我希望我已經(jīng)說(shuō)服你,CS_CLASSDC 是一個(gè)可怕的壞主意。但是,如果它有如此根本性的缺陷,為什么它會(huì)被設(shè)計(jì)出來(lái)?
因?yàn)?16 位 Windows 是協(xié)作式多任務(wù)的。在 16 位世界中,你不必?fù)?dān)心另一個(gè)線程潛入并弄亂你的 DC,因?yàn)檎缥乙呀?jīng)指出的,你正在運(yùn)行的事實(shí)意味著沒(méi)有其他人在運(yùn)行。這整個(gè)多線程災(zāi)難場(chǎng)景不會(huì)發(fā)生,因此 CS_CLASSDC 僅比 CS_OWNDC 稍微古怪。
在單個(gè)進(jìn)程中引入具有多個(gè)線程的先發(fā)制人的多任務(wù)處理將我們帶入了“這沒(méi)有機(jī)會(huì)正常工作”的世界(這段話有點(diǎn)長(zhǎng),再讀一次)。類樣式的存在使得在 16 位代碼中使用它的人可以移植到 Win32(只要他們承諾保持單線程應(yīng)用程序),但現(xiàn)代軟件不應(yīng)該使用它。
總結(jié)
今天的總結(jié)是:CS_CLASSDC 咱哥幾個(gè)是碰都不要碰,誰(shuí)碰誰(shuí)知道。