簡體   English   中英

C ++的簡單多線程混淆

[英]Simple multi-threading confusion for C++

我正在Qt開發一個C++應用程序。 我有一個非常基本的疑問,請原諒我,如果這太愚蠢了......

我應該創建多少個線程來將任務划分到最短時間內?

我問這個是因為我的筆記本電腦是第三代i5處理器(3210米)。 因此它是雙核NO_OF_PROCESSORS 環境變量顯示我4 我在一篇文章中讀到,應用程序的動態內存僅適用於啟動該應用程序的處理器。 那么我應該只創建一個線程(因為env變量說4個處理器)或2個線程(因為我的處理器是雙核和env變量可能暗示核心數)或4個線程(如果那篇文章錯誤)? 請原諒我,因為我是初學級程序員,試圖學習Qt。 謝謝 :)

雖然超線程有點謊言(你被告知你有4個內核,但你真的只有2個內核,另外兩個只能運行前兩個不使用的資源,如果有這樣的話),正確的做法是使用NO_OF_PROCESSORS告訴你的盡可能多的線程

請注意,英特爾並不是唯一一個對你說謊的人,在最近的AMD處理器中,你有6個所謂的“真正”內核更糟糕,但實際上只有4個,其中有資源共享。

但是,大多數時候,它或多或少都有效。 即使沒有明確地阻塞線程(在等待函數或阻塞讀取上),也總是存在一個核心停滯的點,例如由於高速緩存未命中而訪問內存,這會釋放出可供資源使用的資源。超線程核心。

因此,如果你有很多工作要做,並且可以很好地並行化,那么你應該擁有與廣告核心一樣多的工作者(無論它們是“真實的”還是“超級的”)。 這樣,您可以最大限度地利用可用的處理器資源。

理想情況下,可以在應用程序啟動時盡早創建工作線程,並有一個任務隊列將任務交給工作人員。 由於同步通常是不可忽略的,因此任務隊列應該相當“粗略”。 在最大核心使用和同步開銷方面存在權衡。

例如,如果要處理的數組中有1000萬個元素,則可以推送引用100,000或200,000個連續元素的任務(您希望推送1000萬個任務!)。 這樣,你可以確保平均沒有核心保持空閑狀態(如果一個核心提前完成,它會拉動另一個任務而不是什么都不做)而你只有一百個左右的同步,其開銷或多或少都是可忽略不計的。

如果任務涉及文件/套接字讀取或其他可能無限期阻塞的事情,則產生另外1-2個線程通常沒有錯誤(需要一些實驗)。

這完全取決於您的工作負載,如果您的工作負載非常大,那么您應該更接近您的cpu所具有的線程數(在您的情況下為4個 - 2個內核* 2用於超線程)。 一個小的超額訂閱也可能是正常的,因為這可以補償你的一個線程等待鎖或其他東西的時間。
另一方面,如果您的應用程序不依賴於CPU並且大部分都在等待,您甚至可以創建比您的cpu計數更多的線程。 但是你應該注意到線程創建可能是一個很大的開銷。 唯一的解決方案是衡量您的瓶頸是否在這個方向上進行優化。

另請注意,如果您使用的是c ++ 11,則可以使用std::thread::hardware_concurrency來獲取一種可移植的方法來確定您擁有的cpu核心數。

關於動態內存的問題,你必須在那里誤解了一些東西。一般來說,你創建的所有線程都可以訪問你在應用程序中創建的內存。 此外,這與C ++無關,並且超出了C ++標准的范圍。

NO_OF_PROCESSORS顯示4,因為您的CPU具有超線程。 超線程是英特爾的技術商標,它使單個內核能夠或多或少地同時執行同一應用程序的2個線程。 只要例如一個線程正在獲取數據而另一個線程正在訪問ALU,它就可以工作。 如果兩者都需要相同的資源並且指令無法重新排序,則一個線程將停止。 這就是你看到4個核心的原因,即使你有2個核心。

那個動態內存只適用於其中一個Core是IMO不太正確,但注冊內容有時緩存內容是。 駐留在RAM中的所有內容都應該可供所有CPU使用。

比CPU更多的線程可以提供幫助,具體取決於操作系統調度程序的工作方式/訪問數據的方式等。要找到您必須對代碼進行基准測試。 其他一切都只是猜測。

除此之外,如果你想學習Qt,這可能不是正確的擔心...

編輯:

回答你的問題:如果增加線程數,我們無法告訴你程序運行速度會慢多少。 根據你正在做的事情,這將改變。 如果您正在等待來自網絡的響應,則可以更多地增加線程數。 如果您的線程都使用相同的硬件,則4個線程的性能可能不會超過1.最好的方法是簡單地對代碼進行基准測試。

在一個理想的世界中,如果你只是'處理',如果你有4個或8個線程運行,那么數字應該沒有區別,凈時間應該是相同的(忽略上下文切換的時間等),只是響應時間會有所不同。 事情是沒有什么是理想的,我們有緩存,你的CPU都通過同一總線訪問相同的內存,所以最終他們爭奪資源的訪問權限。 然后,您還有一個操作系統可能會或可能不會在給定時間安排線程/進程。

您還要求解釋同步開銷:如果所有線程都訪問相同的數據結構,則必須執行某些鎖定等操作,以便在更新時沒有線程訪問處於無效狀態的數據。

假設你有兩個線程,都做同樣的事情:

int sum = 0; // global variable

thread() {
    int i = sum;
    i += 1;
    sum = i;
}

如果同時啟動兩個線程執行此操作,則無法可靠地預測輸出:可能會發生如下情況:

THREAD A : i = sum; // i = 0
           i += 1;  // i = 1
**context switch**
THREAD B : i = sum; // i = 0
           i += 1;  // i = 1
           sum = i; // sum = 1
**context switch**
THREAD A : sum = i; // sum = 1

最后sum1 ,而不是2即使你開始兩次線程。 為避免這種情況,您必須同步對sum (共享數據)的訪問。 通常,只要需要阻止訪問sum ,就可以這樣做。 同步開銷是線程在資源再次解鎖之前等待的時間,什么都不做。

如果每個線程都有離散的工作包而沒有共享資源,則應該沒有同步開銷。

開始在Qt中的線程之間划分工作的最簡單方法是使用Qt Concurrent框架。 示例:您要對QList中的每個項執行一些操作(非常常見)。

void operation( ItemType & item )
{
  // do work on item, changing it in place
}

QList<ItemType> seq;  // populate your list

// apply operation to every member of seq
QFuture<void> future = QtConcurrent::map( seq, operation );
// if you want to wait until all operations are complete before you move on...
future.waitForFinished();

Qt自動處理線程......無需擔心它。 QFuture文檔描述了如何在需要時使用信號和插槽非對稱地處理map完成。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM