Advertisement

Starting a new c++ thread

Started by November 27, 2024 01:08 PM
15 comments, last by frob 1 day, 20 hours ago

I hope creating several discussions in a short time period is not regarded as an abuse. I have another question, codeproject.com forums are down and I have nowhere to go for help.

I'm looking to find out how you can start a new thread in c++ and check back later to see if the thread is done working and pick up the result. The internet example I found looks a lot like running a function in the same thread rather then a new thread

(I modified the example to fit what I need)

void foo(int A, int B, int * Result)
{

* Result = A + B;
}

int main()
{
int Result;
thread th1(foo, 3,4,&Result);
th1.join(); // I don't want to wait;
cout << "work done the result is ";
cout << Result;
}

My project`s facebook page is “DreamLand Page”

#include <atomic>
#include <vector>
#include <chrono>
#include <thread>
#include <mutex>
#include <string>
#include <iostream>
#include <chrono>
using namespace std;

void thread_func(atomic_bool& stop, atomic_bool &thread_done, vector<string>& vs, mutex& m)
{
	thread_done = false;

	while (!stop)
	{
		m.lock();
		vs.push_back("test");
		m.unlock();
	}

	thread_done = true;
}



int main(void)
{
	atomic_bool stop = false;
	atomic_bool thread_done = false;

	mutex m;
	vector<string> vs;
	thread t(thread_func, ref(stop), ref(thread_done), ref(vs), ref(m));

	auto start_time = std::chrono::system_clock::now();
	auto end_time = start_time + std::chrono::seconds(2);

	while (!stop)
	{
		auto curr_time = std::chrono::system_clock::now();

		if (curr_time >= end_time)
			stop = true;

		cout << "Printing log:" << endl;
		
		m.lock();

		for (vector<string>::const_iterator ci = vs.begin(); ci != vs.end(); ci++)
			cout << *ci << endl;

		vs.clear();

		cout << endl;

		m.unlock();
	}

	while (!thread_done)
	{
		cout << "Waiting for thread" << endl;
		// draw graphics
	}

	cout << "Joining" << endl;

	t.join();

	cout << "Done" << endl;

	return 0;

}

Advertisement

In this code it looks like the thread is starting, doing a simple math operation that finishes almost instantly. The join() function blocks execution on the main thread until the thread has finished and the result is ready.

To keep the main thread running don't call join() until after the work is done. You could have a shared value to signal it is done, or a synchronization primitive like std::atomic if you need extra functionality to keep threads from fighting over memory.

A typical threading learning exercise is to start threads that loop by outputting their thread number and the loop count then sleeping for a moment, while the main thread is looping on something else. You can start several of these worker threads set to run for five seconds or so, then have the main loop run for six or seven. The next learning step is to have an array in the main thread for each worker thread status, have each worker thread set their status when done, and having the main thread occasionally loop through the array to see if all the workers have set their status; when each thread finishes you can join back with it to clean up the resource, until all are done.

Eventually your main thread needs to release the thread resources either by joining back up or detaching the thread. Doing neither can result in zombie threads, technically dead because they have finished their work but not fully dead because their owning process hasn't dealt with their cleanup.

A better way to wait on a result from a thread is std::future. See https://en.cppreference.com/w/cpp/thread/future. You launch an future as async when you want the work to begin, and later, when you need to value, you request it.

// can now use a normal return value
int foo(int A, int B)
{
    return A + B;
}

// start doing the async work
std::future<int> f2 = std::async(std::launch::async, &foo);

// do other work
...

// get the result - will block until the thread finished
const int result = f2.get();

Calin said:
The internet example I found looks a lot like running a function in the same thread rather then a new thread

Your code does use two threads, it's just that the main thread does nothing but waiting, so the multithreading is pointless.

The simplest way to change this is probably:

void foo(int A, int B, int * Result)
{

* Result = A + B;
}

int main()
{
int Result;
thread th1(foo, 3,4,&Result); // spawned thread is working...

int Result2;
foo
(5,6,&Result2); // main thread is working in prallel...

th1.join(); // main thread is done and waits for the spawned thread to finish as well. spawned thread is destroyed.

cout << "work done. Result: "<< Result << " Result2: << Result2;
}

Your code does use two threads, it's just that the main thread does nothing but waiting, so the multithreading is pointless.

I know that. I thought thread has a finish flag that you can check.

Here is a more complex example.

enum Progress
{
    StartThr,
    CheckIfFinished,
    End
};
int TestF(int A,int B, int * Result)
{
    *Result = A + B;
}
Progress Prg;
int Result;
void GameUpdate()
{
    switch (Prg)
    {
    case Progress::StartThr:
        thread th1(TestF, 3,4,&Result);
        Prg = Progress::CheckIfFinished;
        break;
    case Progress::CheckIfFinished:
        if (th1.Finished)
        {
            Prg = Progress::End;
            break;
        }
        else
        {
            break;
        }
    case Progress::End:
        // use Result
    }
}

My project`s facebook page is “DreamLand Page”

Advertisement

Calin said:
I thought thread has a finish flag that you can check.

It has not, but there are many ways to communicate across multiple threads:
Atomics. E.g. thread A sets an atomic flag which other threads can check.
std::mutex can be used to define a critical code section which only one thread at a time can run.
std::condition_variable can be used to notify other threads, or to wake them up when they sleep but new work is ready, fopr example.

That's all i've personally used and needed so far. Idk about newer features such as std::future.

However, keep in mind that many of those language features are about creating new threads on demand.
For games this can be actually too slow, as creating threads is very expensive.
The better way is to create a pool of persistent worker threads just once at application launch, then using those same threads for all work that comes up. Beside the work management itself, this requires to set the threads to sleep if no work is pending, so they do not maximize CPU utilization just from constantly polling for pending work.

It's not easy to find related tutorials. The only one i know is this: https://wickedengine.net/2018/11/simple-job-system-using-standard-c/

This is not an ideal solution, but surely much better than creating threads on demand if you nedd many threads. Good enough for a start, if you're interested.
There are also related libraries, e.g. Intels TBB, but i have never used any of these.
I've heard future C++ standard may add those things, so maybe it's worth to wait.

but there are many ways to communicate across multiple threads

This will take me a while. Things are way more complicated than what I initially thought they would be.

Thank you guys.

My project`s facebook page is “DreamLand Page”

Calin said:
This will take me a while. Things are way more complicated than what I initially thought they would be.

I'm guess it is as simple as you thought, but first you need to figure out how to tell the computer to do what you want. And yes - this will take some time.
Luckily std C++ covers all that's needed these days, and there is no more need to dig into OS or HW details, nor for a library.

I would recommend you create your threads on demand like in your snippets, but keeping in mind that it's not ideal.
Eventually you use more and more multithreading with time, and usage patterns should emerge.
Then you make some job system around those patterns, so it becomes easier to use MT.
Only at this point things like sleeping and condition variables become relevant.

You can simplify it further by assuming threads have no way at all to communicate.
For many things that's no limitation, and you can get some nice speedups from MT without much new skills.

Basically, just create N threads, each doing a chunk of your work, and join them all from the main thread. The main thread is not wasted by this. While waiting, the OS can still assign its CPU core to the pending work from your pool of N.

At some point you will run into limitations, and then you can learn about mutexes, atomics, etc.

If you’re looking for an example code, please check out

https://github.com/sjhalayka/tcpspeed3_multithreaded_listener

Advertisement