Jump to content

Initializing each thread in vector<thread>

Go to solution Solved by fizzlesticks,

"t" is a vector, you're trying create a thread. It would "work" by doing 

t.at(i) = thread(MyFunc, i);

 

But like @Unimportant said, the way you're doing it is a bit inefficient. If MaxThreads is a really small number like 4, it doesn't matter how you do it but it's good to get into the habit of doing things the right way for when you have bigger vectors of more expensive objects.

 

When you know the size of the vector you want, it's typically better to call vector.reserve() to allocate all the memory you need upfront instead of doing multiple reallocations as the vector grows. You should also be using vector.emplace_back instead of push_back. emplace_back creates an object directly into the vector instead of creating an object outside then copying/moving it into the vector. 

 

    vector<thread> t;
    t.reserve(MaxThreads);

    for (int i = 0; i < MaxThreads; i++)
    {
        t.emplace_back(MyFunc, i);
    }

And lastly, don't forget to make your threads unjoinable. Either by calling thread.join() or thread.detach(). If a thread gets destructed while still joinable it will kill your program.

 

 

 

I've successfully created a vector of threads, and now I wanna initialize all the threads. Will the following implementation work?

vector<thread> t(MaxThreads);

for (int i = 0; i < MaxThreads; i++)
{
    t.at(i) = t(MyFunc, i); // Compiler not allowing me to use 't(MyFunc, i)'
}

All suggestions, additions, refinement, etc. appreciated! Thanks!

Nothing to see here ;)

Link to comment
https://linustechtips.com/topic/619862-initializing-each-thread-in-vector/
Share on other sites

Link to post
Share on other sites

std::vector::at performs bounds checking, which is useless overhead when you can be sure you will never go out of bounds as is the case here. Better to use operator [], iterators or preferably range based for loops. If you really want to use std::vector::at, beware it will throw 'std::out_of_range' when the bound check fails, which must be handled.

 

Also, the line "vector<thread> t(MaxThreads);" creates a vector with 'MaxThreads' thread objects, each initialized with their standard constructor, that's a lot of work only to be destroyed and overwritten later in the loop.

 

Simply create a empty vector, and push the initialised threads into it. Notice you are creating a new 'thread' to push into the vector, not a 't' which is the name of the vector.

vector<thread> t;

for (int i = 0; i < MaxThreads; i++)
{
    t.push_back(thread(MyFunc, i));  
}

 

Link to post
Share on other sites

"t" is a vector, you're trying create a thread. It would "work" by doing 

t.at(i) = thread(MyFunc, i);

 

But like @Unimportant said, the way you're doing it is a bit inefficient. If MaxThreads is a really small number like 4, it doesn't matter how you do it but it's good to get into the habit of doing things the right way for when you have bigger vectors of more expensive objects.

 

When you know the size of the vector you want, it's typically better to call vector.reserve() to allocate all the memory you need upfront instead of doing multiple reallocations as the vector grows. You should also be using vector.emplace_back instead of push_back. emplace_back creates an object directly into the vector instead of creating an object outside then copying/moving it into the vector. 

 

    vector<thread> t;
    t.reserve(MaxThreads);

    for (int i = 0; i < MaxThreads; i++)
    {
        t.emplace_back(MyFunc, i);
    }

And lastly, don't forget to make your threads unjoinable. Either by calling thread.join() or thread.detach(). If a thread gets destructed while still joinable it will kill your program.

 

 

1474412270.2748842

Link to post
Share on other sites

9 hours ago, Unimportant said:

std::vector::at performs bounds checking, which is useless overhead when you can be sure you will never go out of bounds as is the case here. Better to use operator [], iterators or preferably range based for loops. If you really want to use std::vector::at, beware it will throw 'std::out_of_range' when the bound check fails, which must be handled.

 

Also, the line "vector<thread> t(MaxThreads);" creates a vector with 'MaxThreads' thread objects, each initialized with their standard constructor, that's a lot of work only to be destroyed and overwritten later in the loop.

 

Simply create a empty vector, and push the initialised threads into it. Notice you are creating a new 'thread' to push into the vector, not a 't' which is the name of the vector.


vector<thread> t;

for (int i = 0; i < MaxThreads; i++)
{
    t.push_back(thread(MyFunc, i));  
}

 

In the following line,

t.push_back(thread(MyFunc, i));

is 'thread' std::thread, or an object of std::thread? Won't the compiler get confused if we name an object 'thread' which is its class name? And what does 'i' do in

thread(MyFunc, i);

Won't this be enough:

thread(MyFunc);

 

Nothing to see here ;)

Link to post
Share on other sites

52 minutes ago, Anand_Geforce said:

is 'thread' std::thread, or an object of std::thread? Won't the compiler get confused if we name an object 'thread' which is its class name?

thread is the constructor for the thread type, it creates and returns an object of thread which then gets moved into the vector. You're not naming anything, it's essentially just calling a function. 

 

Quote

And what does 'i' do

The thread constructor can take multiple arguments. The first is the function to call and the rest get passed as arguments to that function.

 

void func(int a, int b, const char* s)
{
	//some code
}

thread t(func, 1, 2, "some string");

In that code the thread t will call func with the arguments 1, 2, "some string" as if you wrote 

func(1, 2, "some string");
Quote

Won't this be enough:

If MyFunc doesn't take any arguments, yes.

1474412270.2748842

Link to post
Share on other sites

3 hours ago, fizzlesticks said:

thread is the constructor for the thread type, it creates and returns an object of thread which then gets moved into the vector. You're not naming anything, it's essentially just calling a function. 

 

The thread constructor can take multiple arguments. The first is the function to call and the rest get passed as arguments to that function.

 


void func(int a, int b, const char* s)
{
	//some code
}

thread t(func, 1, 2, "some string");

In that code the thread t will call func with the arguments 1, 2, "some string" as if you wrote 


func(1, 2, "some string");

If MyFunc doesn't take any arguments, yes.

 

Thanks a ton! You were really helpful!

Nothing to see here ;)

Link to post
Share on other sites

7 hours ago, fizzlesticks said:

thread is the constructor for the thread type, it creates and returns an object of thread which then gets moved into the vector. You're not naming anything, it's essentially just calling a function. 

 

The thread constructor can take multiple arguments. The first is the function to call and the rest get passed as arguments to that function.

 


void func(int a, int b, const char* s)
{
	//some code
}

thread t(func, 1, 2, "some string");

In that code the thread t will call func with the arguments 1, 2, "some string" as if you wrote 


func(1, 2, "some string");

If MyFunc doesn't take any arguments, yes.

Oh-oh! Another problem - how do I join() the threads, as they are in a vector?

Nothing to see here ;)

Link to post
Share on other sites

2 hours ago, fizzlesticks said:

You loop through the vector and call join on each one.

But, if I do the following:

for(i=0; i < MaxThreads; i++)
{
	t[i].join();	// BTW, is this correct?
}

won't the main thread, wait for the first child thread to finish, then wait for the second child thread to finish and so on? Is there a way to make it wait for all the child threads at the same time? (All threads finish at exactly the same time)

Nothing to see here ;)

Link to post
Share on other sites

17 minutes ago, Anand_Geforce said:

won't the main thread, wait for the first child thread to finish, then wait for the second child thread to finish and so on? Is there a way to make it wait for all the child threads at the same time? (All threads finish at exactly the same time)

Yes, that's exactly what it will do. There's no problem with a thread finishing before you call join on it, so it doesn't matter when or in what order you do it as long as it gets done before the threads are deconstructed.

 

If you call t[0].join(), the rest of the threads are either already finished and waiting for you to call join on them or still working. It doesn't make any difference if you're waiting for t[0] or t[99] all the threads that are still running are doing their work and they all have to finish before you can continue anyway.

1474412270.2748842

Link to post
Share on other sites

16 hours ago, fizzlesticks said:

Yes, that's exactly what it will do. There's no problem with a thread finishing before you call join on it, so it doesn't matter when or in what order you do it as long as it gets done before the threads are deconstructed.

 

If you call t[0].join(), the rest of the threads are either already finished and waiting for you to call join on them or still working. It doesn't make any difference if you're waiting for t[0] or t[99] all the threads that are still running are doing their work and they all have to finish before you can continue anyway.

So since I know that they will start at the same time and take exactly 2 minutes, then can I simply place this in the main thread?

Sleep(120000); // Sleep for 2 mins...

 

Nothing to see here ;)

Link to post
Share on other sites

11 hours ago, Anand_Geforce said:

So since I know that they will start at the same time and take exactly 2 minutes, then can I simply place this in the main thread?


Sleep(120000); // Sleep for 2 mins...

 

You could but that's a waste of a thread. Why not create N-1 new threads and do the last job in the main thread?

1474412270.2748842

Link to post
Share on other sites

On 5/7/2016 at 0:34 AM, fizzlesticks said:

You could but that's a waste of a thread. Why not create N-1 new threads and do the last job in the main thread?

I've done just that, and it works great! However, I would like to know how to handle the return values of a threaded function - in short:

thread(MyFunc, i);

if MyFunc returns a long instead of a void, how do I assign this return value to a variable (that is already declared at the beginning)?

Nothing to see here ;)

Link to post
Share on other sites

24 minutes ago, Anand_Geforce said:

if MyFunc returns a long instead of a void, how do I assign this return value to a variable (that is already declared at the beginning)?

You can't, threads don't return anything. If you want to keep what you already have, you could use a global array/vector and have the threads put the values in there then read them back from the main thread. Or do it proper way and use std::future and std::async which are essentially threads that return things.

1474412270.2748842

Link to post
Share on other sites

10 minutes ago, fizzlesticks said:

You can't, threads don't return anything. If you want to keep what you already have, you could use a global array/vector and have the threads put the values in there then read them back from the main thread. Or do it proper way and use std::future and std::async which are essentially threads that return things.

Seems complicated... but I'll try! Thanks!

Nothing to see here ;)

Link to post
Share on other sites

std::future and std::promise is a way to do it indeed.

Another way would be to use a function object for the thread in stead of a plain function. The function object can store data as member data and can be interrogated after the threads end.

Link to post
Share on other sites

13 minutes ago, Unimportant said:

std::future and std::promise is a way to do it indeed.

Another way would be to use a function object for the thread in stead of a plain function. The function object can store data as member data and can be interrogated after the threads end.

Can you show me how to pass a function pointer to a thread object?

Nothing to see here ;)

Link to post
Share on other sites

Following example demonstrates how to use a function object or functor as the payload for a std::thread in stead of a plain function. When the threads are finished we extract the result from the function object. This is a rather silly example off course, but in a real program the function object can hold a large state (many, many member variables) that can be extracted in stead of the single return argument of a function.

 

#include <iostream>
#include <vector>
#include <thread>
#include <functional>

class ExampleFunctionObject
{
public:

	ExampleFunctionObject() : LoopCnt(0) {}

	//Delete copy constructor so compiler warns us when we forget to pass a std::ref to thread. 
	ExampleFunctionObject(const ExampleFunctionObject&) = delete;

	//Providing "operator ()" makes this a function object, the object can be
	//called asif it were a function.
	void	operator() ()
	{
		//For this example, we just make LoopCnt count to a million. The volatile specifier...
		//...on int LoopCnt makes it so the compiler cannot optimize away the loop.
		while (LoopCnt < 1000000)
			LoopCnt++;	
	}

	int	GetLoopCnt() const { return LoopCnt; }

private:

	volatile int LoopCnt;
};


int main()
{
	//Create 4 function objects in vector.
	std::vector<ExampleFunctionObject>	FunctionObjects(4);

	//Start a thread for each function object. We use std::ref
	//because we want the thread to start on the function object itself, not a copy.
	std::vector<std::thread> 	Threads;
	for (auto& FunctionObject : FunctionObjects)
		Threads.push_back(std::thread(std::ref(FunctionObject)));

	//Join threads.
	for (auto& Thread : Threads)
		Thread.join();

	//Print results.
	int i = 0;
	for (auto& FunctionObject : FunctionObjects)
		std::cout << "Thread " << ++i << " result : " << FunctionObject.GetLoopCnt() << '\n';	

	return 0;
}

 

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×