2013-11-27 52 views
3

我的程序讀入兩個輸入文件並交替將行寫入輸出文件。我有它,所以它按正確的順序寫入(第一個文件,然後第二個,然後再一次,....),但問題是它在每個文件中最後兩次寫入最後一個字符。讀取兩個文件並使用線程輸出到另一個文件

#include <iostream> 
#include <fstream> 
#include <thread> 
#include <mutex> 
using namespace std; 


mutex mtx; 
int turn = 1; 

void print_line(ifstream * in_stream, ofstream * out_stream, int t); 

int main(int argc, const char * argv[]){ 
    ifstream input_file_1; 
    ifstream input_file_2; 
    ofstream output_file; 

    input_file_1.open("input_1"); 
    input_file_2.open("input_2"); 
    output_file.open("output"); 

    if (input_file_1.fail() || input_file_2.fail() || output_file.fail()) { 
     cout << "Error while opening the input files\n"; 
     exit(EXIT_FAILURE); 
    } 
    else{ 
     thread input1 (print_line, &input_file_1, &output_file, 1); 
     thread input2 (print_line, &input_file_2, &output_file, 2); 
     input1.join(); 
     input2.join(); 
    } 
    input_file_1.close(); 
    input_file_2.close(); 
    output_file.close(); 

    return 0; 
} 

void print_line(ifstream * in_stream, ofstream * out_stream, int t){ 
    char temp; 
    while (!in_stream->eof()) { 
     mtx.lock(); 
     if (turn == t) { 
      *in_stream>>temp; 
      *out_stream<<temp; 
      if (turn == 1) { 
       turn = 2; 
      } 
      else{ 
       turn = 1; 
      } 
     } 
     mtx.unlock(); 
    } 
} 

輸入1

a 
c 
e 

輸入2

b 
d 
f 

輸出

abcdefef 

我不知道爲什麼它再次寫入的最後一個字符,也就是有一個更好的方法來使用線程來完成排序部分,I瞭解互斥鎖用於確保兩個線程不會同時寫入,但是如何確保線程在線程2之前執行並確保其保持交替?
感謝

回答

2

的從std::ifstream直到EOF達到讀取正確和慣用的方法是:

char temp; 
while(in_stream >> temp) { 
    // Will only be entered if token could be read and not EOF. 
} 

與此相比。 假設所有,但最後一個字符已經從流閱讀:

while(!in_stream.eof()) { 
    in_stream >> temp; // 1st iteration: reads last character, STILL NOT EOF. 
         // 2nd iteration: tries to read but reaches EOF. 
         //    Sets eof() to true. temp unchanged. 
         //    temp still equal to last token read. 
         //    Continue to next statement... 
    /* More statements */ 
} 

其次,你的函數print_line有關於同步的一些問題。解決它的一種方法是使用std::condition_variable。這裏有一個例子:

condition_variable cv; 

void print_line(ifstream& in_stream, ofstream& out_stream, int t){ 
    char temp; 
    while (in_stream >> temp) { 
     unique_lock<mutex> lock(mtx); // Aquire lock on mutex. 

     // Block until notified. Same as "while(turn!=t) cv.wait(lock)". 
     cv.wait(lock, [&t] { return turn == t; }); 
     out_stream << temp; 
     turn = (turn == 1) ? 2 : 1; 
     cv.notify_all(); // Notify all waiting threads. 
    } 
} 

正如你在上面的例子中看到的,我也通過流作爲引用而不是指針。傳遞指針很容易出錯,因爲它可能通過nullptr(NULL值)。

要將流作爲引用傳遞給std::thread的構造函數,必須將它們包裝在參考包裝器std::ref中,例如,是這樣的:(的拷貝std::thread構造函數的參數)

thread input1(print_line, ref(input_file_1), ref(output_file), 1); 

Live example(略經修改成使用標準IO代替fstream


一些其他的事情:

1。不必要的代碼在main

ifstream input_file_1; 
ifstream input_file_2; 
ofstream output_file; 

input_file_1.open("input_1"); 
input_file_2.open("input_2"); 
output_file.open("output"); 

使用構造採取直接的文件名,而不是在這裏使用open

ifstream input_file_1("input_1"); 
ifstream input_file_2("input_2"); 
ofstream output_file("output"); 

2.使用慣用的方法來檢查,如果流是準備閱讀:

if (!input_file_1 || !input_file_2 || !output_file) { 

3.在這種情況下無需使用close,因爲dtor會關閉資源(依靠RAII)。

input_file_1.close(); // \ 
input_file_2.close(); // } Unnecessary 
output_file.close(); ///

4.你的設計是有點差進一步訪問任何數據流或turnmain功能將導致數據爭。

()寧可不using namespace std污染命名空間,而不是(例如std::ifstream)在任何地方使用完全合格的名稱。可選擇在相關範圍內聲明using std::ifstream等。

+0

解決了閱讀額外字符的問題,但是現在我並不總是得到abcdef,有時會讓它們失序或者只是輸入1的第一個字母。之前當我擁有它時,我一直以其他方式獲得abcdefef 。任何線索爲什麼? –

+0

@GregBrown查看我提出的解決方案的更新答案。 – Snps

+0

非常感謝你的回答,一切都很好,你是否介意擴大cv.wait(lock,[&t] {return turn == t;});我知道使用[&t] {return turn == t; }在他們的論點。 –

1

關於EOF:有一個相當不錯的解釋在這裏:How does ifstream's eof() work?

關於鎖定:鎖定只爲你做的輸出的時間,以減少鎖爭用和切換turn變量。

除此之外,我認爲這是一個可怕的設計。我甚至不知道,如果一個C++流可以在線程中以這種方式使用,但即使如此,我仍然懷疑這是一個好主意。

相關問題