多線程競爭內存


1 內存中一地址,同一時間,只能被單一線程訪問還是可以被多線程並行訪問
2 開發一個程序的所有線程都在一個核心里執行還是可以被多核分開執行
3 程序中新開一個線程執行靜態方法是將另外分一片內存同時將方法copy過去嗎

 

1 內存中一地址,同一時間,只能被單一線程訪問還是可以被多線程並行訪問
對於x86這樣的共享存儲,競爭總線的方式,“同一時間”,當然只能有一個線程訪問。但是要明確,可能其他線程會訪問這個內存數據的臟數據的緩存,也可能因為對內存的訪問缺乏原子性而存在同步問題。
2 開發一個程序的所有線程都在一個核心里執行還是可以被多核分開執行
如果這些線程沒有相關性,那么OS會調度它們在多個內核/處理器上運行。
3 程序中新開一個線程執行靜態方法是將另外分一片內存同時將方法copy過去嗎
當然不是,不同的線程擁有自己的線程上下文(其實就是寄存器值)和自己的堆棧,至於代碼,是共享的。

 

是這樣的,現代的處理器具有緩存,甚至多層次的緩存,如果多個線程要同時訪問同一個內存的數據,那么為了保證程序不出錯,所有的線程都必須等待,不是說不能訪問,而是說這樣做的代價很高。我們假設你的程序中有1/10的cpu時間在訪問這個內存的數據,你有32個並行的線程,那么很顯然,你的理論性能上限較之單線程程序為1/(0.9/32+0.1)=7.8,而且隨着處理器的增多,這樣的浪費更大,無論你采用1百個CPU還是1萬個CPU,都不可能超過10。而我們理想中期待如果有32個CPU,那么程序能快32倍,如果有128個CPU能快128倍。

問題三我說了,操作系統只為每個線程開辟線程上下文和堆棧的空間。

 

注意我說的是1/10的cpu時間,如果你有100行代碼,有1行訪問了競爭的內存,那么因為內存訪問需要的周期遠遠超過寄存器存取和指令,那么cpu時間都會超過1/10。而且由於緩存同步的需要,程序實際上執行的更慢。甚至可能出現,用2個cpu還算不過1個cpu的情況。

 

 

堆: 是大家共有的空間,分全局堆和局部堆。全局堆就是所有沒有分配的空間,局部堆就是用戶分配的空間。堆在操作系統對進程初始化的時候分配,運行過程中也可以向系統要額外的堆,但是記得用完了要還給操作系統,要不然就是內存泄漏。


棧:是個線程獨有的,保存其運行狀態和局部自動變量的。棧在線程開始的時候初始化,每個線程的棧互相獨立,因此,棧是 thread safe的。每個C ++對象的數據成員也存在在棧中,每個函數都有自己的棧,棧被用來在函數之間傳遞參數。操作系統在切換線程的時候會自動的切換棧,就是切換 SS/ESP寄存器。棧空間不需要在高級語言里面顯式的分配和釋放
 
總結: java局部變量中存的是對象引用,單例情況下 沒有類共享變量的情況下, 每個方法的執行 內部是局部變量,由線程執行這個方法,互不影響;代碼是共享的,但每個線程方法棧中的局部變量參數是不一樣的。
 
每一個線程都獨立擁有一個棧,多個線程可以“同時”執行。CPU執行程序代碼完全依靠各種寄存器。當一個線程將被掛起時,當前的 各種寄存器的數值就被存儲在了線程的棧中。當CPU重新執行此線程時,將從棧中取出寄存器的數值,接着運行,好像這個線程從來就沒有被打斷過一樣。正是因為每個線程都有一個獨立的棧,使線程擁有了可以“閉門造車”的能力。只要 將參數傳遞給線程的棧,CPU將擔負起這塊內存存儲區的管理工作,並適時地執行線程函數代碼對其進行操作。當系統在多個線程間切換時,CPU 將執行相同的代碼操作不同的棧。
下面舉一個例子來加深理解。
隨着面向對象編程方法的普及,我們很樂意將任何操作都包裝成為一個類。線程函數也不例外,以靜態函數的形式將線程函數放在類中是C++編程普遍使用的一種方法。通常情況下對象包括屬性(類變量)與方法(類函數)。屬性指明對象自身的性質,方法用於操作對象,改變它的屬性。現在有一個小問題要注意了。類的靜態函數只能訪問類的靜態變量,而靜態變量是不屬於單個對象的,他存放在進程的全局數據存儲區。一般情況下,我們希望每個對象能夠“獨立”,也就是說,多個對象能夠各自干各自的工作,不要相互打擾。如果以通常的方法,以類(靜態)變量存儲對象的屬性,可就要出問題了,因為類(靜態)變量不屬於單個對象。現在怎么辦呢?如何繼續保持每個對象的“獨立性”。解決的方法就是使用棧,將參數傳遞給線程函數的局部變量(棧存儲區),以單個對象管理每個線程,問題就解決了。當然了,解決方法是多種多樣的,這里只是為了進一步解釋多線程與對象的關系。
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM