我的程序读入两个输入文件并交替写入输出文件。我有它所以它以正确的顺序写入(第一个文件,然后第二个,然后再次,....)但问题是它在最后两次写入每个文件中的最后一个字符。
#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之前执行并确保它保持交替? 谢谢
答案 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 ++流,但即便如此,我也怀疑这是一个好主意。