(Google面試題)有四個線程1、2、3、4同步寫入資料……C++11實現

來源:互聯網
上載者:User

標籤:signed   數組   else   同步   c++   size   wait   需要   public   

最近在學習多線程,題目源自 MoreWindows先生的 《秒殺多線程第一篇》(http://blog.csdn.net/morewindows/article/details/7392749)

題目摘錄:

第五題(Google面試題)

有四個線程1、2、3、4。線程1的功能就是輸出1,線程2的功能就是輸出2,以此類推.........現在有四個檔案ABCD。初始都為空白。現要讓四個檔案呈如下格式:

A:1 2 3 4 1 2....

B:2 3 4 1 2 3....

C:3 4 1 2 3 4....

D:4 1 2 3 4 1....

請設計程式。

代碼實現如下:
 1 #include <iostream> 2 #include <string> 3 #include <thread> 4 #include "Semaphore.h" 5  6 constexpr unsigned MAX_COUNT = 10; 7 constexpr unsigned MAX_THREAD = 4; 8  9 unsigned getNext(unsigned current, bool order) {10     if (order) {11         unsigned next = current + 1;12         return  next % MAX_THREAD; // //正序13     } else {14         unsigned next = current - 1;15         return next % MAX_THREAD; //反序16     }17 }18 19 class File20 {21 public:22     File(const std::string& name = "0") 23     :m_name(name)24     ,m_buffer(MAX_COUNT, ‘0‘)25     ,m_pos(0){26         27     }28 29     void write(char c) {30         m_buffer.at(m_pos++) = c;31     }32 33     std::string getName() const {34         return m_name;35     }36     std::string getData() const {37         return m_buffer;38     }39 private:40     std::string m_name;41     std::string m_buffer;42     size_t m_pos;43 };44 45 Semaphore t1(1), t2(1), t3(1), t4(1);46 Semaphore* semaphoreArray[MAX_THREAD] = { &t1, &t2, &t3, &t4 };47 File files[MAX_THREAD] = { File("A"), File("B"), File("C"), File("D") };48 unsigned currentFile[MAX_THREAD] = { 0, 1, 2, 3 }; //每個線程當前應該寫入的檔案id49 50 void write(unsigned threadId) {51     const char c = ‘1‘ + threadId;52     auto& fid = currentFile[threadId];53     //auto fid = threadId;54     auto nextThreadId = getNext(threadId, true); //正序55     unsigned count = 0;56     while (MAX_COUNT != count) {57         semaphoreArray[threadId]->wait();58         //寫檔案        59         files[fid].write(c);60         //將檔案傳遞給下一個線程。61         semaphoreArray[nextThreadId]->signal();62         //更新要寫入的檔案id63         fid = getNext(fid, false); //逆序64         ++count;65     }66 }67 68 69 int main() {70     std::cout << "hello" << std::endl;71 72     //create thread73     std::thread threads[MAX_THREAD];74     for (unsigned i = 0; i != MAX_THREAD; ++i) {75         threads[i] = std::thread(write, i);76     }77 78     //join79     for (auto& thread : threads) {80         thread.join();81     }82 83     //output84     for (const auto& file: files) {85         std::cout << file.getName() << ": " << file.getData() << std::endl;86     }87 88     std::cout << "bye" << std::endl;89     return 0;90 }

其中Semaphore.h的代碼見:http://www.cnblogs.com/waterfall/p/7966116.html

 

運行結果:

代碼解析:

首先,本代碼用File類類比檔案寫入,方便列印調試。void File::write(char c) 方法即在檔案結尾追加字元c。

根據題目要求可以看出,當檔案(比如檔案A)被線程1寫入結束後,需要被下一個線程即線程2寫入。因此會有A: 1 2 3 4 1...  B,C,D檔案同理,只不過初始寫入線程不同。

在代碼種利用 :

unsigned currentFile[MAX_THREAD] = { 0, 1, 2, 3 }; //每個線程當前應該寫入的檔案id

指定每個線程初始寫入哪個檔案。如果不想配置也可以用threadId作為線程初始寫入的檔案id(write函數中被注釋掉的那一句),他們剛好相等。

代碼中用訊號量表示線程可執行寫入的次數。write函數會在寫入完一個檔案後,自動計算下一個要寫入的檔案。順序為A,D,C,B,A....,逆序。

Semaphore t1(1), t2(1), t3(1), t4(1); //用於設定訊號量Semaphore* semaphoreArray[MAX_THREAD] = { &t1, &t2, &t3, &t4 }; //放入數組只是為了方便調用

如果只是給線程1的訊號量設為1,其他都設為0。線程的健全狀態可以看作:

step1: 線程1 訊號量1 A->D     線程2 訊號量0 B 阻塞    線程3 訊號量0 C 阻塞  線程4 訊號量0 D 阻塞

step2: 線程1 訊號量0 D  阻塞  線程2 訊號量1 B->A      線程3 訊號量0 C 阻塞  線程4 訊號量0 D 阻塞

step3: 線程1 訊號量0 D  阻塞  線程2 訊號量0 A 阻塞    線程3 訊號量1 C->B    線程4 訊號量0 D 阻塞

step4: 線程1 訊號量0 D  阻塞  線程2 訊號量0 A 阻塞    線程3 訊號量0 B 阻塞  線程4 訊號量1 D->C

第一輪運行結束:線程1在檔案A中寫入了1,線程2在檔案B中寫入了2,線程3在檔案C中寫入了3,線程4在檔案D中寫入了4。

之後第二輪開始:線程1在檔案D中寫入了1,線程2在檔案A中寫入了2……

以此類推,相當於同一時間只有1個線程在工作。每當線程工作時,將字元寫入自己當前要寫入的檔案中,並計算出下一個要寫入的檔案。

這種情況顯然是不會出現衝突的。

 

如果將線程1的初始訊號量置為2:

step1: 線程1 訊號量2 A->D  線程2 訊號量0 B 阻塞  線程3 訊號量0 C 阻塞  線程4 訊號量0 D 阻塞

step2: 線程1 訊號量1 D->C  線程1 訊號量1 B->A    線程3 訊號量0 C 阻塞  線程4 訊號量0 D 阻塞

可以看出檔案D中被寫入了字元1,出現錯誤。原因在於檔案D當前期待被線程4寫入,在4寫入之前處於未就緒狀態。此時被任何其他線程寫入都是錯誤的。

 

如果假設線程1和線程4的初始訊號量各為1,其他為0。且假設線程4先運行

step1.1: 線程1 訊號量1 A 就緒     線程2 訊號量0 B 阻塞  線程3 訊號量0 C 阻塞  線程4 訊號量1 D->C

step1.2: 線程1 訊號量2 A->D  線程2 訊號量0 B 阻塞  線程3 訊號量0 C 阻塞  線程4 訊號量0 C 阻塞

step2.1: 線程1 訊號量1 D->C  線程1 訊號量1 B->A    線程3 訊號量0 C 阻塞  線程4 訊號量0 D 阻塞

檔案D先被線程4寫入字元4,之後再被線程1寫入字元1便符合題目要求了。(D的序列應該為:4 1 2 3 4……)

也就是說,線程1的訊號量的增加,是基於前一個線程已經完成檔案寫入了。此時檔案處於就緒狀態,可以被下一個線程使用,也即把檔案傳遞給下一個線程。

 

所以最終的代碼中,將每一個線程的初始訊號量設定為1。可滿足題目要求。哪怕任何線程在執行中出現阻塞(可用sleep類比),也不影響其他線程運行。

假設線上程1中執行一個長時間的sleep。可能的運行情況如下

step0: 線程1 訊號量1 A 就緒     線程2 訊號量1 B 就緒  線程3 訊號量1 C 就緒  線程4 訊號量1 D 就緒

一段時間後……

step1:線程1 訊號量4 A 就緒      線程2 訊號量0 A 阻塞  線程3 訊號量0 A 阻塞  線程4 訊號量0 A 阻塞

所有的線程都在等待將資料寫入檔案A中。但只有線程1先在檔案A中寫入字元1,之後釋放訊號,B才能繼續寫入。之後是C,D。依然沒有衝突。檔案A也滿足1,2,3,4的字元順序。

其他:

目前網上搜到的很多代碼,同一時間只允許一個線程進行檔案的寫入,或者是通過計數,一輪結束後才進行下一輪寫入。本代碼真正實現了線程間的並行。只有無檔案可寫時才會進行等待。

謝謝。

(Google面試題)有四個線程1、2、3、4同步寫入資料……C++11實現

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.