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

Reading two files and outputting to another file using threads

本文关键字:文件 线程 输出 另一个 两个 读取      更新时间:2023-10-16

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

#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 filesn";
        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之前执行,并确保它保持交替
感谢

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);

实时示例(略微修改为使用标准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.您的设计有些糟糕,因为进一步访问main函数中的任何流或turn都会导致数据竞争。

5.)最好不要用using namespace std污染命名空间,而是在所有地方使用完全限定的名称(例如std::ifstream)。可选择在相关范围内声明using std::ifstream等。

关于EOF:这里有一个很好的解释:ifstream';s of()工作?

关于锁:只在进行输出时锁定,以减少锁争用和切换turn变量。

除此之外,在我看来,这是一个非常可怕的设计。我甚至不确定C++流是否可以以这种方式跨线程使用,但即便如此,我也怀疑这是否是个好主意。