读取两个文件并使用线程输出到另一个文件

时间:2013-11-27 19:51:31

标签: c++ multithreading

我的程序读入两个输入文件并交替写入输出文件。我有它所以它以正确的顺序写入(第一个文件,然后第二个,然后再次,....)但问题是它在最后两次写入每个文件中的最后一个字符。

#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

我不知道为什么它再次写入最后一个字符,也有更好的方法来使用线程进行排序部分,我知道使用互斥锁来确保两个线程不会同时写入但是,如何保证线程1在线程2之前执行并确保它保持交替? 谢谢

2 个答案:

答案 0 :(得分: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的ctor复制参数)

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。您的设计有点差,因为进一步访问任何流或turn函数中的main将导致数据竞争。

5。)不希望使用using namespace std污染名称空间,而是在任何地方使用完全限定名称(例如std::ifstream)。可选择在相关范围内声明using std::ifstream等。

答案 1 :(得分:1)

关于EOF:这里有一个相当不错的解释:How does ifstream's eof() work?

关于锁定:仅在您输出时锁定,以减少锁争用并切换turn变量。

除此之外,在我看来,这是一个很糟糕的设计。我甚至不确定,如果可以在线程中使用C ++流,但即便如此,我也怀疑这是一个好主意。