I need to create console application that counts files in folders. Each folder executes parallel. I get directories paths from .txt file and put them to threads.
I'm using std::thread and boost::filesystem.
It works fine with one directory, but crashes or returns wrong results with many.
What is interesting that last thread always gets correct result, but those before it are wrong.
Here is my code:
DirHandler.h
#include <iostream>
#include <fstream>
#include <string>
#include <thread>
#include <windows.h>
#include <boost/filesystem.hpp>
using namespace std;
using namespace boost::filesystem;
class DirHandler{
public:
DirHandler();
void getPaths(ifstream file);
void output();
static void run_thread(pair<string, int> * dir, string cur_path, void *args)
{
DirHandler *prunnable = static_cast<DirHandler*>(args);
prunnable->some_counting(dir, cur_path);
}
private:
vector<thread> threads;
vector<pair<string, int>> paths; // current directory name and files amount
void some_counting(pair<string, int> * dir, string cur_path);
void escape();
};
DirHandler.cpp
void DirHandler::getPaths(ifstream file)
{
// parse pathes and create separate thread for each
string line;
if (file.is_open())
{
while (getline(file, line))
{
cout << line << endl;
// add thread for path
pair<string, int> dir = make_pair(line, 0);
paths.push_back(dir);
threads.push_back(thread(&DirHandler::run_thread, &paths.back(), line, this));
}
for (auto& thr : threads){
thr.join();
}
file.close();
}
}
void DirHandler::some_counting(pair<string, int> * dir, string cur_path){...}
main.cpp
#include <iostream>
#include <windows.h>
#include "DirHandler.h"
int main(int argc, char* argv[])
{
DirHandler dirHandler = DirHandler();
dirHandler.getPaths(ifstream("strings.txt")); //ifstream(argv[1])
dirHandler.output();
return 0;
}
Note : While debugging I've found that all streams before last have id=0 in the end
Also I've read that the problem can be caused by a reference to single object. (In my case it's vector<pair<string, int>> paths)
So my question is how to get few threads work correctly ?
The problem is that you get pointers from the paths vector, and if the vector is resized all previous pointers (and iterators) to elements in the vector will become invalid. Using an invalid pointer leads to undefined behavior which often leads to crashes.
And one common way of causing a vector is to adding new elements to it, exactly what you do in your loop.
One solution is to either pass a reference to the vector and an index of the element to the thread (the index will not change on reallocation). Another solution is to have two loops, one to add all elements to the vector and the second to create the threads.
There are other solutions too, like not passing a pointer to the thread, but instead pass the pair by value. This is actually the solution I recommend.
After checking your code a little, I also see that since you use std::thread you don't need the static member function wrapper at all. Instead you can call the non-static actual thread function directly:
threads.emplace_back(&DirHandler::some_counting, this, paths.back(), line);
[Note the use of this as the second argument, as well as the change to emplace_back for the threads vector]
Well, the most obvious problem is here: threads.push_back(thread(&DirHandler::run_thread, &paths.back(), line, this)); you are using pointer to the object(&paths.back()) which might not be there anymore after any push_back. So this error should be fixed, you can't pass pointer to the vector item unless you can guarantee that it will not reallocate its internal structure.
Besides that and ugly code I don't see anything. But more problems might be lurking in the some_counting method.
Related
I'm new to C++ and trying to understand a simple example of inserting a list of integers into a map.
#include <iostream>
#include <map>
#include <list>
using namespace std;
map<string, list<int>> m;
void insert(list<int>& list_to_insert)
{
m.insert({"ABC", list_to_insert});
}
void setup()
{
std::list<int> local_list = { 7, 5, 16, 8 };
insert(local_list);
}
int main()
{
setup();
cout << m["ABC"].size(); // PRINTS 4
}
As far as my understanding, local_list is a variable only known to the setup function. When I pass in a reference to the insert function, I expect it to work. However, at the end of setup, I expect local_list to be removed from the stack frame. However, in my main function, when I call size(), I see that it has in fact persisted throughout. I am not sure I understand how local_list gets persisted at the end of setup(). What is exactly happening here?
map<string, list<int>> m;
contains lists, not references to lists.
So your
m.insert({"ABC", list_to_insert});
will create a copy of the passed list.
PS: why-is-using-namespace-std-considered-bad-practice
Hopefully my title isn't too confusing. I'm trying to write a sound manager for my game using SFML. I'm trying to replace my new/delete with the "smart pointer" std::shared_ptr. This is what I have so far.
/* SoundManager.h */
#ifndef SOUNDMANAGER_H
#define SOUNDMANAGER_H
#include <SFML/Audio.hpp>
#include <string>
#include <memory>
class SoundManager
{
public:
~SoundManager();
struct jteSound
{
sf::Sound snd;
sf::SoundBuffer sndBuffer;
std::string name;
};
//Load a new sound from path and store it in audio bank bnk.
//Banks are simply std::vectors of type jteSound.
void registerNewSound(std::vector<std::shared_ptr<jteSound>> &bnk, std::string path, std::string sndName);
//Footsteps bank
std::vector<std::shared_ptr<jteSound>> bnkFootsteps;
};
#endif // SOUNDMANAGER_H
/* SoundManager.cpp */
#include "SoundManager.h"
#include <stdlib.h>
SoundManager::~SoundManager()
{
/*
//Cleanup each sound bank that we use.
for (std::vector<jteSound*>::iterator it = bnkFootsteps.begin(); it != bnkFootsteps.end(); ++it) {
delete *it;
}
*/
}
void SoundManager::registerNewSound(std::vector<std::shared_ptr<jteSound>> &bnk, std::string path, std::string sndName)
{
static int counter = 0;
for (int i = counter; counter <i+1; counter++) {
bnk.push_back(jteSound);
bnk[i]->name = sndName;
bnk[i]->sndBuffer.loadFromFile(path);
bnk[i]->snd.setBuffer(bnk[i]->sndBuffer);
}
}
bnk.push_back(jteSound); gives me a compiler error. If I remove the line, the program compiles, but crashes. I have tried things like emplace_back() or jteSound* or new jteSound, but nothing works. I always get a lengthy compiler error or immediate runtime crash. When I use regular pointers and new/delete, see https://bpaste.net/show/fa684f2f2d5e and https://bpaste.net/show/c74ac701ce7a, the code works as expected. Any thoughts appreciated!
The type of the elements inside your std::vector is std::shared_ptr<jteSound> which means that std::vector::push_back will accept only instances of that type.
To make your code work you have two options. The first is using std::make_shared helper function as follows:
bnk.push_back(std::make_shared<jteSound>());
// the equivalent counterpart is:
bnk.push_back(std::shared_ptr<jteSound>(new jteSound));
The second is using std::vector::emplace as follows:
bnk.emplace(bnk.end(), new jteSound);
As the comments below warn, using the second option is risky because it can cause memory leak when the new jteSound succeeds but the std::vector::emplace has to reallocate memory and fails.
I have many calls to a function that takes just one argument and I don't want update those calls. But I want to call that function from some other special place but in that case it should additionally fill a vector that I will pass with some data.
I know I can create a default argument with NULL pointer to a std::vector container and then, if it is null, skip doing any extra actions and if it is a valid pointer - gather data to vector. However I wanted to try using boost::optional.
Please see the code below. It compiles and works, but Is this approach fine or I shouldn't do that and better use raw pointer?
#include <boost/optional.hpp>
#include <boost/none_t.hpp>
#include <vector>
//header file declaration
int doAction(
int value,
char *msg = NULL,
boost::optional<std::vector<int>&> optionalNumberVec = boost::none);
//main.cpp
int doAction(int value, char* msg, boost::optional<std::vector<int>&> optionalNumberVec)
{
//do main actions here
//...
//...
//end of main action
//get additional information to table
if (optionalNumberVec)
{
optionalNumberVec.get().push_back(5);
optionalNumberVec.get().push_back(3);
}
return 1;
}
int main()
{
std::vector<int> numVec;
boost::optional<std::vector<int>&> optionalNumberVec(numVec);
doAction(2);
doAction(2, NULL, optionalNumberVec);
return 0;
}
Using boost or not is a simple decision based on your preferences (or your boss's preferences).
Once you get used to C++ you will notice that it doesn't really matter which one you use, as long you know how to use them.
What is the most correct and efficient way to std::move elements from a vector of a certain type (T1) into a vector of an std::pair of that same type (T1) and another type (T2)?
In other words, how should I write MoveItems()?
#include <iostream> // For std::string
#include <string> // For std::string
#include <vector> // For std::vector
#include <utility> // For std::pair
using std::vector;
using std::string;
using std::pair;
vector<string> DownloadedItems;
vector<pair<string,bool>> ActiveItems;
vector<string> Download()
{
vector<string> Items {"These","Words","Are","Usually","Downloaded"};
return Items;
}
void MoveItems()
{
for ( size_t i = 0; i < DownloadedItems.size(); ++i )
ActiveItems.push_back( std::pair<string,bool>(DownloadedItems.at(i),true) );
}
int main()
{
DownloadedItems = Download();
MoveItems();
return 0;
}
Thank you for your time and help, I truly appreciate it!
void MoveItems()
{
ActiveItems.reserve(DownloadedItems.size());
for (auto& str : DownloadedItems)
ActiveItems.emplace_back(std::move(str), true);
}
N.B.: For strings as small as the ones in your example, moving may have the same cost as copying due to SSO, or perhaps even slightly more expensive if the implementation decides to empty out the source anyway.
Some things you can do:
At the start of MoveItems(), call ActiveItems.reserve(DownloadedItems.size());. This prevents your array from resizing while you push things into it.
Instead of calling push_back call emplace_back. Here is an explanation of the advantages of doing so.
Worth noting, in this example, you can stop the copy into a new data structure by just constructing the std::pair from the start, and not copying data.
I am new to c++ but not to programming, and I am having a very baffling problem. I have class which in its constructor creates a list.
As you can see, I am printing out the final value of the list two different ways, which generally agree with each other: one using list::end, and the other using list::back. Then I call the constructor to this class in my main function, access the list that was created, and try to print the final value. The sample code is given below.
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <typeinfo>
#include <list>
#include <algorithm>
#include <queue>
using namespace std;
class Process{
public:
Process(int CB);
int CB;
};
Process::Process(int c){
CB = c;
}
class Event{
public:
Event(Process *process);
Process *process;
};
Event::Event(Process *ps){
process = ps;
}
typedef list<Event> EventList;
class DES{
public:
DES(string originFile);
EventList events;
};
DES::DES(string originFile){
ifstream infile (originFile.c_str());
string str;
while (getline(infile, str)) {
// output the line
//cout << str << endl;
istringstream iss(str);
int AT,TC,CB,IO;
if (!(iss >> AT >> TC>>CB>>IO)) {
cout<<"breaking out of while loop \n";
break;
}
Process p(CB);
Event evt(&p);
this->events.push_back(evt);
}
int cb = this->events.back().process->CB;
EventList::iterator inserter2 = this->events.begin();
EventList::iterator inserter3 = this->events.end();
//inserter3--;
//cout<<"CB after while loop using List<>::end(): " <<inserter3->process->CB<<endl;
//cout<<"CB after while loop using LIST<>::back "<<cb<<endl;
infile.close();
}
int main (int argc, char* argv[]) {
string inputFileName = argv[1];
DES des(argv[1]);
EventList::iterator b = des.events.end();
b--;
cout<<"CB at back of list in main: "<<b->process->CB<<endl;
return 0;
}
So here is where I get confused. The print statement in main should match the output of the print statements in teh constructor, since they are all simply printing the field ->process->CB of the last element of the list. However, for some reason this only works when I uncomment the line //EventList::iterator inserter2 = this->events.begin(); in my constructor. Similarly, if I keep that line and instead comment out the line EventList::iterator inserter3 = this->events.end();, it also doesn't work. Only when I construct an iterator on BOTH the end and beginning of the list does the correct value get printed out in main.
Can anyone shed some light on this odd behavior? I know it must be some simple misunderstanding due to my lack of familiarity with c++, but I have to admit this behavior seems a bit unnatural to me.
EDIT: here is the output with one of the iterators in the constructor commented out:
CB after while loop using List<>::end(): 10
CB after while loop using LIST<>::back 10
CB at back of list in main: 306496
And here is the output with both of the iterators in the constructor:
CB after while loop using List<>::end(): 10
CB after while loop using LIST<>::back 10
CB at back of list in main: 10
-Paul
You're using / storing the address of a local here:
Process p(AT,TC,CB,IO);
Event evt(AT,&p,CREATED,READY);
Once the code block that declares p is exited, any referral to that pointer results in undefined behavior.
Since Process contains a simple int, you could just store copies of Process instead of using pointers.
class Process{
public:
Process(int CB);
int CB;
};
class Event{
public:
Event(const Process& process);
Process process;
};
Event::Event(const Process& ps) : process(ps) {}
Then the first block of code would look like this:
Process p(CB);
Event evt(p);
or simply:
Event evt(Process(CB));
This should at least rid you of the inconsistent results.
If you really do need a pointer, consider smart pointers such as std::unique_ptr or if deemed necessary, std::shared_ptr instead of using raw pointers.