色偷偷91综合久久噜噜-色偷偷成人-色偷偷尼玛图亚洲综合-色偷偷人人澡久久天天-国内精品视频一区-国内精品视频一区二区三区

Hello! 歡迎來到小浪云!


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


avatar
小浪云 2025-04-17 16
  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的任務,要么完成,要么不完成,不會出現中間狀態。這種不會出現中間狀態,結果可預期的特性稱為原子性。也就是說,加鎖的本質是為了實現原子性。

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

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

相關閱讀

主站蜘蛛池模板: 天天干妹子 | 成人黄色片视频 | 日韩特黄的片 | 四虎在线视频免费观看视频 | 亚洲精品三区 | 黄页免费视频播放在线播放 | 国产精品成人第一区 | 日本久久久久亚洲中字幕 | 久久精品人人做人人 | 欧美精品99久久久久久人 | 欧美亚洲国产精品 | 天天操天天舔 | 国产专区第一页 | 国产临盆孕妇孕交中出视频 | 天天操天天搞 | 天天插日日射 | 偷偷鲁国内视频视频在线 | 日韩按摩片一级 | 国内精品手机在线观看视频 | 久久99精品麻豆国产 | 在线看一区二区 | 日韩精品在线一区 | 免费370理论片中文字幕 | 欧美亚洲一区二区三区导航 | 久久精品一区二区 | 日日拍夜夜嗷嗷叫狠狠 | 国产伦精品一区二区三区 | 女高中生被cao到哭视频 | 久久久久久久国产 | 精品一精品国产一级毛片 | av香港经典三级级 在线 | 久久影院视频 | 美女免费黄网站 | 精品久久成人免费第三区 | 色亚洲影院 | 国产偷国产偷亚洲高清午夜 | 一级做a爰片性色毛片16美国 | 精品在线视频一区 | 国产精品久久久久亚洲 | 欧美麻豆久久久久久中文 | 国产v综合v亚洲欧美大片 |