九色91_成人精品一区二区三区中文字幕_国产精品久久久久一区二区三区_欧美精品久久_国产精品99久久久久久久vr_www.国产视频

Hello! 歡迎來到小浪云!


【Linux】同步與異步的魔法:如何讓多線程程序更高效


avatar
小浪云 2025-04-17 17
  1. 線程搶票

實現多線程搶票的思路很簡單:假設有1000張票,讓5個線程去搶,直到票數為0為止。

代碼語言:c++

#include <iostream> #include <unistd.h> #include <pThread.h> <h1>define N 5</h1><p>using namespace std;</p><p>int ticket = 1000;</p><p>void<em> pthreadRun(void</em> arg) { char<em> name = static_cast<char</em>>(arg); int sum = 0; while (true) { if (ticket > 0) { usleep(2000); --ticket; cout << name << "搶到一張票,還剩" << ticket << "張票" << endl; ++sum; } else { cout << name << "共搶到" << sum << "張票" << endl; break; } } return nullptr; }</p><p>int main() { pthread_t tids[N]; char* name[N] = {"thread-1", "thread-2", "thread-3", "thread-4", "thread-5"};</p><pre class="brush:php;toolbar:false">for (int i = 0; i < N; i++) {     int ret = pthread_create(&tids[i], nullptr, pthreadRun, (void*)name[i]);     if (ret != 0) {         cout << "pthread_create error: error_code=" << ret << endl;     } }  for (int i = 0; i < N; i++) {     pthread_join(tids[i], nullptr); }  return 0;

}

在運行上述程序時,我們會發現最終票數居然變成了負數。然而,代碼看起來并沒有明顯的錯誤。這是為什么呢?

  1. 原因分析 – 資源共享問題

在上面的搶票程序中,全局變量ticket是線程的共享資源。要修改ticket,需要執行以下三個步驟:

  1. 將ticket從內存拷貝到寄存器中。
  2. 在CPU內完成計算。
  3. 從寄存器中將結果轉移回內存。

【Linux】同步與異步的魔法:如何讓多線程程序更高效

在單線程情況下,這三個步驟似乎沒什么問題,因為計算機的速度非常快,用戶幾乎感覺不到延遲。然而,在多線程環境中,線程對共享資源的訪問會存在競爭現象。

假設有兩個線程,分別為thread1和thread2。在某個時刻,thread1準備修改ticket,將ticket拷貝到寄存器中時,thread2可能會搶占CPU,導致thread1的操作被中斷。大多數情況下,這種情況不會發生,因為CPU的計算速度非常快,通常能完成所有操作。但是在搶票程序中,由于存在休眠操作(usleep),這種情況確實發生了。

當ticket等于1時,滿足循環中的條件(ticket > 0)。假設此時thread-1在執行該操作,進入if語句后,執行休眠。但CPU可能不會立即開始休眠,而是選擇運行下一個線程,假設是thread-2。由于ticket的值還沒有被修改,仍然等于1,thread-2也滿足if條件。其他線程同樣如此。過了一段時間,thread-1醒來,開始執行ticket–操作,其他線程隨后醒來也會執行ticket–操作,最終導致票數變成負數。

即使去掉usleep,負數情況的概率也會很低,但仍然可能發生。正確的解決方案是使用鎖。

  1. 知識補充 – 臨界資源

多線程場景中,像ticket這樣的可以被多個線程訪問的共享資源稱為臨界資源。涉及對臨界資源進行操作的代碼區域稱為臨界區。

代碼語言:C++

int ticket = 1000; // 臨界資源</p><p>void<em> pthreadRun(void</em> arg) { char<em> name = static_cast<char</em>>(arg); int sum = 0; while (true) { // 臨界區開始 if (ticket > 0) { usleep(2000); --ticket; cout << name << "搶到一張票,還剩" << ticket << "張票" << endl; ++sum; } else { cout << name << "共搶到" << sum << "張票" << endl; break; } // 臨界區結束 } return nullptr; }

臨界資源的本質是多線程共享資源,而臨界區是涉及共享資源操作的代碼區域。

  1. 知識補充 – ‘鎖’

為了安全地訪問臨界資源,必須確保在使用時的安全性,這就是鎖的作用。用生活中的例子來說,鎖就像是進入房間的鑰匙,只有持有鑰匙的人才能進入房間。

對于臨界資源也是如此,為了訪問時的安全,可以通過加鎖來實現。實現多線程間的互斥訪問,互斥鎖是解決多線程并發訪問問題的手段之一。具體操作就是:在進入臨界區之前加鎖,離開臨界區之后解鎖。

還是以前面的搶票程序為例。假設此時正在執行的線程為thread-1,當它在訪問ticket時如果進行了加鎖,在thread-1被切走后,假設此時進入的線程為thread-2,thread-2無法對ticket進行操作,因為此時鎖被thread-1持有,thread-2只能堵塞式等待鎖,直到thread-1解鎖。因此,對于thread-1來說,在加鎖環境中,只要接手了訪問臨界資源ticket的任務,要么完成,要么不完成,不會出現中間狀態。這種不會出現中間狀態,結果可預期的特性稱為原子性。也就是說,加鎖的本質是為了實現原子性。

在加鎖的同時,我們還需要注意以下幾點:

  • 加鎖、解鎖是比較耗費系統資源的,會在一定程度上降低程序的運行速度。
  • 加鎖后的代碼是串行執行的,勢必會影響多線程場景中的運行速度。
  • 為了盡可能降低影響,加鎖粒度要盡可能地細。

相關閱讀

主站蜘蛛池模板: 国产一区999 | 欧美日韩一区在线 | 国产高清精品在线 | 蜜臀91视频 | 一区二区三区国产 | 久久精品亚洲一区二区三区浴池 | 亚洲精品日韩一区二区电影 | 国产精品久久久 | 色性av| 91久久精品国产 | 成人免费在线小视频 | 国产精久久久久久 | 亚洲精品久久久久中文字幕欢迎你 | 国产高清精品在线 | 亚洲免费人成在线视频观看 | 成人一级毛片 | 日韩精品 电影一区 亚洲 | 久久久91精品国产一区二区三区 | 三区四区在线观看 | 欧美极品在线观看 | 天天综合永久入口 | 日韩有码在线观看 | 国产精品久久久久久久久久妞妞 | www.蜜桃av | 久久久久久久久国产精品 | 欧美激情一区二区 | 日本一区二区电影 | 色吊丝2288sds中文字幕 | 91精品国产91久久久久久吃药 | 在线观看亚洲专区 | 久久综合一区二区 | 蜜桃在线视频 | 欧美日韩综合精品 | 国产在线精品一区 | 欧美精品在线观看 | 欧美精品久久久 | 久久国际精品 | 亚洲麻豆| 午夜精品一区 | 免费视频一区二区三区在线观看 | 国产精品99久久久久久www |