Вместо порождение нового потока для каждой задачи, почему бы не просто сформировать несколько рабочих потоков, а затем использовать std:packaged_task
и std::future
чтобы одновременно вычислять вещи без накладных расходов на нерест нового потока
например.:
class Calculator {
public:
Calculator() : m_bDoneFlag(false) {
for(auto& thread : m_arrayThreads)
{
thread = std::thread([this]{
std::unique_lock<std::mutex> lockGuard(m_mutex, std::defer_lock);
while(!m_bDoneFlag)
{
lockGuard.lock();
m_condTaskWaiting.wait(lockGuard, [this]{ return !m_queueTasks.empty(); });
std::packaged_task<void*()> packagedTask = std::move(m_queueTasks.front());
m_queueTasks.pop();
lockGuard.unlock();
// Execute task:
packagedTask();
}
});
}
}
~Calculator()
{
m_bDoneFlag = true;
std::unique_lock<std::mutex> lockGuard(m_mutex);
m_queueTasks.emplace([]{ std::this_thread::sleep_for(std::chrono::milliseconds(100)); return nullptr; });
m_queueTasks.emplace([]{ std::this_thread::sleep_for(std::chrono::milliseconds(100)); return nullptr; });
lockGuard.unlock();
m_condTaskWaiting.notify_all();
for(auto& thread : m_arrayThreads)
{
thread.join();
}
}
std::future<void*> AddTask(std::function<void*()> funcToAdd)
{
std::packaged_task<void*()> packagedTask(funcToAdd);
std::future<void*> future = packagedTask.get_future();
std::unique_lock<std::mutex> lockGuard(m_mutex);
m_queueTasks.emplace(std::move(packagedTask));
lockGuard.unlock();
m_condTaskWaiting.notify_one();
return future;
}
private:
std::mutex m_mutex;
std::array<std::thread, 2> m_arrayThreads;
std::queue<std::packaged_task<void*()>> m_queueTasks;
std::condition_variable m_condTaskWaiting;
std::atomic<bool> m_bDoneFlag;
};
Вы можете использовать это как так:
int main()
{
Calculator myCalc;
std::future<void*> future1 = myCalc.AddTask([]{ std::string* pszTest = new std::string("Test String"); return pszTest; });
std::future<void*> future2 = myCalc.AddTask([]{ std::complex<float>* pcmplxTest = new std::complex<float>(5.0f, 10.5f); return pcmplxTest; });
std::string* pszTest = reinterpret_cast<std::string*>(future1.get());
std::complex<float>* pcmplxTest = reinterpret_cast<std::complex<float>*>(future2.get());
std::cout << *pszTest << " and " << *pcmplxTest << std::endl;
delete pszTest;
delete pcmplxTest;
return 0;
}
Очевидно, что это не имеет безопасности типа мы хотим, и вы можете улучшить безопасность типов значительно, если вы можете сузить тип возврата значение, которое вам всегда необходимо, чтобы предотвратить возврат указателя на void
.
Без блокировки, если этот код будет вызываться дважды в одно и то же время, вы можете создать 2 потока. Если вы вызываете это только из одного конкретного потока, это не проблема. – asafrob
std :: invokeWhenLastDoneWhatExactly? –