2

I'm new to multithreading and C++ and I'm having a problem when trying to use threads in my application which save files. The code is as follows:

#include <iostream> #include <thread> #include <fstream> #include <vector> #include <sstream> using namespace std; void writeCSV(vector<vector<double> > & vec, const char* filename) { ofstream output(filename); for (vector<vector<double> >::const_iterator i = vec.begin(); i != vec.end(); ++i) { for (vector<double>::const_iterator j = i->begin(); j != --i->end(); ++j) { output << *j << ", "; } output << *(--i->end()) << "\n"; } } void testFn(int id) { std::ostringstream filename; vector<vector<double> > v(460, vector<double>(460,0)); filename << "test" << id << ".csv"; const char* fileStr = filename.str().c_str(); cout << id << " : " << fileStr << endl; writeCSV(v, fileStr); } int main() { int numOfThreads = 180; std::thread t[numOfThreads]; for (int i= 0; i< numOfThreads; i++) { t[i] = std::thread (testFn, i); } for (int i = 0; i< numOfThreads; i++) { t[i].join(); } return 0; } 

When I run this program, it prints out in the terminal (subsection of results):

66 : 0�c 97 : test97.csv 90 : �'�dz 85 : �'�dz 43 : 9695 : �'�dz 67 : �'�dz 93 : : �_ �� 115 : test115.csv 144 : test144.csv 99 : test99.c0 68 : 91 : )� 98 : test98.c0 

as well as saving the files with weird/wrong filenames. This seems to be an issue from multithreading and ostringstream I guess, but any ideas why/how to fix?

3
  • Sounds like data race. Commented Jul 5, 2016 at 13:40
  • 1
    No, it is not a data race. Speculation rarely leads to the right answer. Commented Jul 5, 2016 at 13:41
  • 1
    stackoverflow.com/questions/21034834/… Commented Jul 5, 2016 at 14:02

2 Answers 2

8

This has nothing to do with multithreading.

const char* fileStr = filename.str().c_str(); 

std::ostringstream's str() method returns a std::string representing the contents of the string stream.

A std::string's c_str() method returns an internal pointer to the string data.

What you're missing is that the pointer returned by c_str() is only valid until either the std::string is modified, or it is destroyed, whichever comes first.

Here std::string gets destroyed immediately, because it is a temporary value. As such, a pointer to its internal data is immediately invalidated.

You must store the returned string in an object, that exists as long as the string data is needed. Simply:

std::string str = filename.str(); const char* fileStr = str.c_str(); 

str continues to exist for the remainder of its automatic scope, which is long enough, here.

Sign up to request clarification or add additional context in comments.

Comments

5

In your file initiialisation code, you have the following excerpt:

std::ostringstream filename; ... filename << "test" << id << ".csv"; const char* fileStr = filename.str().c_str(); 

That final line is a killer. What you're doing is taking your hard-earned filename, getting the string representation from it, extracting the c_str() pointer from it - and then destroying the temporary string object. From that point on, any attempt to access fileStr is fraught.

What you need to do is assign the temporary string object to a local variable:

filename << "test" << id << ".csv"; std::string fileString = filename.str(); const char* fileStr = fileString.c_str(); 

That guarantees the lifetime of the temporary string object - at least until the end of the function!

Without doing that, fileStr points into the middle of the unallocated heap - and the heap changes underneath it.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.