Beginner Multithreading Help

Hello,

Firs I must apologize for creating this topic and hopefully I wont upset anyone with my noobiness. I’m looking for some inputs so feel free to tell me what I’m doing wrong.

Say we wanna do a bunch of calculation in the processor (not in the audio block or real time) and the nature of these calculations allows us to do them in parallel. We can save some time by doing them in separate threads. Here is my journey so far.
Using std::threads is working great and cuts the time almost in half. But is this safe? What happens if the processor is deleted half way through? (no need to get into locking at this point).

void JuceAudioProcessor::doStuff()
{
	std::thread t1([this]()
		{
			//do a bunch of things
		});
	std::thread t2([this]()
		{
			//do a bunch of things
		});		
	t1.join();
	t2.join();
	//resume and use the result when both done
}

Then I looked into Thread::lunch but I wasn’t hungry, so I tried Thread::launch, and found out it works a bit differently and you cant really wait or join things in a similar way. So after few hours ended up with ThreadPool.

myThreadPool.addJob(std::function<juce::ThreadPoolJob::JobStatus()>([this]() -> juce::ThreadPoolJob::JobStatus 
{
	//do a bunch of things
	return ThreadPoolJob::jobHasFinished;
}));
myThreadPool.addJob(std::function<juce::ThreadPoolJob::JobStatus()>([this]() -> juce::ThreadPoolJob::JobStatus 
{
	//do a bunch of things
	return ThreadPoolJob::jobHasFinished;
}));
while (myThreadPool.getNumJobs() > 0)
	{//wait}
//resume and use the result when both done

It’s not as clean looking but maybe I’ll need the (extra) features to do this properly?

There is also the issue of waiting. If you call it from the UI things will freeze.
That mean we shouldn’t wait, do things another way, or do the entire thing on another thread. That lead me to this janky experiment, which I dont know why even works? How are we creating threads inside another thread? Maybe someone knowledgeable can explain that. This feels so wrong to me hehe

void JuceAudioProcessor::doStuff()
{
	Thread::launch([this]()
	{
		std::thread t1([this]()
		{
			//do a bunch of things
		});
		std::thread t2([this]()
		{
			//do a bunch of things
		});		
		t1.join();
		t2.join();
		//resume and use the result when both done
	}
}

How do you handle things like this?
I’m curious to hear your thoughts and advice.
Thank you.