在我的Windows消息循环中,我正在调用std :: thread来计算某些东西(游戏特定的东西)。我想禁止循环创建下一个线程,直到计算出他必须计算的内容为止。现在我正在这样处理:
if( !mIsCalculating ) {
mIsCalculating = true;
std::thread th( Test::method, this );
th.detach();
}
void Test::method() {
// ...
mIsCalculating = false;
}
但我想知道std库中是否存在现有的解决方案,比如std :: invokeWhenLastDone? ; - )
答案 0 :(得分:2)
不是为每个任务生成一个新线程,为什么不只是形成一些工作线程,然后使用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;
}
显然,这不具备我们想要的类型安全性,如果您可以缩小返回值的类型,可以显着提高类型安全性,以防止必须返回指针{ {1}}。
答案 1 :(得分:1)
据我所知,这种方法不存在。 但是,您可以通过让单个线程永远运行并在Test :: Method中管理所需的重复来解决您的问题。 当需要新的计算时,主循环可以通过使用std :: condition_variable来通知Test :: Method。
答案 2 :(得分:1)
从技术上讲,mIsCalculating
应该是一个原子值,这样你就不会遇到在两个不同线程中使用非原子变量的问题。除此之外,假设在“UI线程”中使用它,它只能从一个线程调用,所以应该是可以接受的。
还有一些替代解决方案,例如具有永久运行的单个线程并将数据提供给管道,消息队列或使用事件来发出信号“更多可用工作,以及类似事件”,这是“结果” 。