一:背景 1. 講故事 最近也是奇怪,在社區里看到好幾篇文章聊static的玩法以及怎麼拿這個和麵試官扯半個小時,有點意思,點進去看都是java版的,這就沒意思了,怎麼也得有一篇和麵試官扯C#中的 static用法撒,既然沒有人開這個頭,那我就獻醜了。。。,下麵以QA的方式記述,大家可以代入一下能回 ...
一:背景
1. 講故事
最近也是奇怪,在社區里看到好幾篇文章聊static的玩法以及怎麼拿這個和麵試官扯半個小時,有點意思,點進去看都是java版的,這就沒意思了,怎麼也得有一篇和麵試官扯C#中的 static用法撒,既然沒有人開這個頭,那我就獻醜了。。。,下麵以QA的方式記述,大家可以代入一下能回答幾個問題。
二:QA環節
1. 第一個問題
- 面試官: 請問您都是在什麼場景下用static的?
解析: 可能面試官潛意識的想問問你會不會使用本地緩存。
- 碼農: 先不說我的場景,縱觀C#的底層FCL源碼,你會發現很多的 static修飾的集合,如ThreadPool:
[SecurityCritical]
private static bool QueueUserWorkItemHelper(WaitCallback callBack, object state, ref StackCrawlMark stackMark, bool compressStack)
{
QueueUserWorkItemCallback callback = new QueueUserWorkItemCallback(callBack, state, compressStack, ref stackMark);
ThreadPoolGlobals.workQueue.Enqueue(callback, forceGlobal: true);
result = true;
}
其中的 workQueue 就是一個靜態隊列,不僅如此還有Quartz底層自研的線程池,還有web中的Session,Application,無非就是想用static做一個池化技術和AppDomain級的本地緩存,所以我的應用場景也無非是這些了。
2. 第二個問題
- 面試官: 您會幾種實現單例的方式?
解析:既然面試官想和你扯static,就是想看看你會不會用 static cctor靜態構造器構建單例!
- 碼農: 實不相瞞,不管是用懶漢式還是餓漢式,大體上也就這幾種 雙檢鎖, static cctor, Lazy
, 不知道您想讓我細說哪一種?
3. 第三個問題
- 面試官: 那就說一下靜態構造函數為什麼可以實現單例?
解析: 可能覺得碼農回答的有點拽,問深一點看看是不是唬人的。
- 碼農:說到單例,每一個人都會提到在多線程場景下的併發問題導致多個單例的尷尬,所以有了給代碼加上各種花哨的鎖,比如剛纔我提到的
雙檢索
,所以說沒有鎖。。。這個問題是搞不定的,換句話說靜態構造函數
也是用了鎖機制。
4. 第四個問題
- 面試官: 你確定用到了鎖? 有證據嗎?
解析: 有戲了,對你產生感興趣了,願聽其詳。
- 碼農: 既然要證據,那我先構思一段如下代碼:
class Program
{
static void Main(string[] args)
{
Person person = new Person();
Console.ReadLine();
}
}
class Person
{
static Person()
{
Console.WriteLine("正在處理靜態函數");
Console.ReadLine();
}
}
然後抓一個dump文件,用windbg看一下主線程的托管和非托管堆棧。
0:000> ~0s
ntdll!NtReadFile+0x14:
00007ff8`8d2eaa64 c3 ret
0:000> !dumpstack
OS Thread Id: 0x4ac0 (0)
Current frame: ntdll!NtReadFile+0x14
Child-SP RetAddr Caller, Callee
000000c119bfdcd0 00007ff817090957 (MethodDesc 00007ff816f85aa8 +0x37 ConsoleApp6.Person..cctor()), calling (MethodDesc 00007ff8741140b8 +0 System.Console.ReadLine())
000000c119bfdd10 00007ff8765e6c93 clr!CallDescrWorkerInternal+0x83
000000c119bfdd18 00007ff87660a51c clr!ListLockEntry::FinishDeadlockAwareEnter+0x40, calling clr!GetThread
000000c119bfdd50 00007ff8765e6b79 clr!CallDescrWorkerWithHandler+0x4e, calling clr!CallDescrWorkerInternal
000000c119bfdd80 00007ff87390d663 clrjit+0x1d663, calling clrjit+0x1be60
000000c119bfdd90 00007ff87660c56b clr!DispatchCallDebuggerWrapper+0x1f, calling clr!CallDescrWorkerWithHandler
000000c119bfddf0 00007ff87660c535 clr!DispatchCallSimple+0x93, calling clr!DispatchCallDebuggerWrapper
000000c119bfde40 00007ff87660a5b9 clr!MethodTable::EnsureInstanceActive+0x110, calling clr!DomainFile::EnsureLoadLevel
000000c119bfde90 00007ff87660bf65 clr!MethodTable::RunClassInitEx+0x111, calling clr!DispatchCallSimple
000000c119bfdec0 00007ff88d350119 ntdll!RtlDebugFreeHeap+0x2a9, calling ntdll!RtlLeaveCriticalSection
000000c119bfdee0 00007ff88d2b77a2 ntdll!RtlInitializeCriticalSection+0xa2, calling ntdll!_security_check_cookie
000000c119bfdf80 00007ff87660a51c clr!ListLockEntry::FinishDeadlockAwareEnter+0x40, calling clr!GetThread
000000c119bfdfc0 00007ff87660c15c clr!MethodTable::DoRunClassInitThrowing+0x3b9, calling clr!MethodTable::RunClassInitEx
000000c119bfe810 00007ff8765f08b4 clr!ListLockEntry::`scalar deleting destructor'+0xd4, calling clr!operator delete
000000c119bfff10 00007ff88d044034 KERNEL32!BaseThreadInitThunk+0x14, calling KERNEL32!guard_dispatch_icall_nop
000000c119bfff40 00007ff88d2c3691 ntdll!RtlUserThreadStart+0x21, calling ntdll!guard_dispatch_icall_nop
仔細看上面的代碼,你會發現有很多處 ListLockEntry
,這就和鎖扯上了關係哈,這算證據不?
5. 第五個問題
- 面試官: 小伙子windbg玩的挺溜,那請回答一下靜態變數是存在哪的,有什麼證據嗎?
解析:轉變思路,開始證據先行了