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