Billy
Billy

Reputation: 409

stringstream and multithreading

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?

Upvotes: 2

Views: 3317

Answers (2)

Sam Varshavchik
Sam Varshavchik

Reputation: 118435

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.

Upvotes: 8

John Burger
John Burger

Reputation: 3672

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.

Upvotes: 5

Related Questions