C++ 终止工作线程和主线程之间的争用条件
我在从主线程终止工作线程时遇到问题。到目前为止,我尝试的每种方法要么导致竞争条件,要么导致死锁 工作线程存储在名为ThreadPool的类中的内部类中,ThreadPool使用unique_ptr维护这些工作线程的向量 以下是我的线程池的标题:C++ 终止工作线程和主线程之间的争用条件,c++,multithreading,threadpool,deadlock,race-condition,C++,Multithreading,Threadpool,Deadlock,Race Condition,我在从主线程终止工作线程时遇到问题。到目前为止,我尝试的每种方法要么导致竞争条件,要么导致死锁 工作线程存储在名为ThreadPool的类中的内部类中,ThreadPool使用unique_ptr维护这些工作线程的向量 以下是我的线程池的标题: class ThreadPool { public: typedef void (*pFunc)(const wpath&, const Args&, Global::mFile_t&, std::mutex&, std:
class ThreadPool
{
public:
typedef void (*pFunc)(const wpath&, const Args&, Global::mFile_t&, std::mutex&, std::mutex&); // function to point to
private:
class WorkerThread
{
private:
ThreadPool* const _thisPool; // reference enclosing class
// pointers to arguments
wpath _pPath; // member argument that will be modifyable to running thread
Args * _pArgs;
Global::mFile_t * _pMap;
// flags for thread management
bool _terminate; // terminate thread
bool _busy; // is thread busy?
bool _isRunning;
// thread management members
std::mutex _threadMtx;
std::condition_variable _threadCond;
std::thread _thisThread;
// exception ptr
std::exception_ptr _ex;
// private copy constructor
WorkerThread(const WorkerThread&): _thisPool(nullptr) {}
public:
WorkerThread(ThreadPool&, Args&, Global::mFile_t&);
~WorkerThread();
void setPath(const wpath); // sets a new task
void terminate(); // calls terminate on thread
bool busy() const; // returns whether thread is busy doing task
bool isRunning() const; // returns whether thread is still running
void join(); // thread join wrapper
std::exception_ptr exception() const;
// actual worker thread running tasks
void thisWorkerThread();
};
// thread specific information
DWORD _numProcs; // number of processors on system
unsigned _numThreads; // number of viable threads
std::vector<std::unique_ptr<WorkerThread>> _vThreads; // stores thread pointers - workaround for no move constructor in WorkerThread
pFunc _task; // the task threads will call
// synchronization members
unsigned _barrierLimit; // limit before barrier goes down
std::mutex _barrierMtx; // mutex for barrier
std::condition_variable _barrierCond; // condition for barrier
std::mutex _coutMtx;
public:
// argument mutex
std::mutex matchesMap_mtx;
std::mutex coutMatch_mtx;
ThreadPool(pFunc f);
// wake a thread and pass it a new parameter to work on
void callThread(const wpath&);
// barrier synchronization
void synchronizeStartingThreads();
// starts and synchronizes all threads in a sleep state
void startThreads(Args&, Global::mFile_t&);
// terminate threads
void terminateThreads();
private:
};
实际的线程循环
void ThreadPool::WorkerThread::thisWorkerThread()
{
_thisPool->synchronizeStartingThreads();
try
{
while (!_terminate)
{
{
_thisPool->_coutMtx.lock();
std::cout << std::this_thread::get_id() << " Sleeping..." << std::endl;
_thisPool->_coutMtx.unlock();
_busy = false;
std::unique_lock<std::mutex> lock(_threadMtx);
_threadCond.wait(lock);
}
_thisPool->_coutMtx.lock();
std::cout << std::this_thread::get_id() << " Awake..." << std::endl;
_thisPool->_coutMtx.unlock();
if(_terminate)
break;
_thisPool->_task(_pPath, *_pArgs, *_pMap, _thisPool->coutMatch_mtx, _thisPool->matchesMap_mtx);
_thisPool->_coutMtx.lock();
std::cout << std::this_thread::get_id() << " Finished Task..." << std::endl;
_thisPool->_coutMtx.unlock();
}
_thisPool->_coutMtx.lock();
std::cout << std::this_thread::get_id() << " Terminating" << std::endl;
_thisPool->_coutMtx.unlock();
}
catch (const std::exception&)
{
_ex = std::current_exception();
}
_isRunning = false;
}
void ThreadPool::WorkerThread::thisWorkerThread()
{
_此池->同步启动读取();
尝试
{
而(!\u终止)
{
{
_thisPool->_coutMtx.lock();
标准::cout matchemap_mtx);
_thisPool->_coutMtx.lock();
std::cout get()->_thisThread.detach();
//若线程抛出异常,则在main中重新抛出它
if(it->get()->exception()!=nullptr)
std::rethrow_异常(it->get()->exception());
}
}
最后是调用线程池的函数(scan函数在main上运行)
//递归扫描路径以查找所选扩展类型的所有文件,调用线程来解析文件
unsigned int Functions::Scan(wpath路径、常量Args和Args、线程池和池)
{
wre草书目录迭代器d(路径),e;
未签名的整数filesFound=0;
while(d!=e)
{
if(args.verbose())
std::wcout path());
}
}
++d;
}
std::cout我不理解线程终止和连接的问题
加入线程就是等待给定的线程终止,因此这正是您想要做的。如果线程已经完成执行,join
将立即返回
因此,您只需要在终止
调用期间加入每个线程,就像您在代码中所做的那样
注意:当前,如果刚终止的线程有活动的异常,\u ptr
,您将立即重新引发任何异常。这可能会导致未连接的线程。在处理这些异常时,您必须记住这一点
更新:查看代码后,我看到一个潜在的错误:std::condition\u variable::wait()
在发生虚假唤醒时可以返回。如果是这种情况,您将在上次工作的路径上再次工作,导致错误的结果。您应该为新工作设置一个标志,该标志在添加新工作时设置,并且\u threadCond.wait(锁定)
行应该在一个循环中,用于检查标志和\u terminate
。但不确定该循环是否能解决您的问题。问题有两个方面:
synchronizeStartingThreads()有时会阻塞1或2个线程,等待OK(while(某些条件)barrierCond.wait(锁定)中的一个问题)。该条件有时不会计算为true。删除while循环修复了此阻塞问题
第二个问题是工作线程可能会进入_threadMtx,并且在进入_threadCond.wait()之前调用了notify_all,因为已经调用了notify,线程将永远等待
即
上述代码只在20%的时间内有效。每隔一段时间,当我有连接时,程序会无限期挂起。如果我删除连接,则不会出现死锁,但main会在线程之前终止。连接以某种方式导致死锁。是否可能是两个或多个线程之间解除锁定?它们是否共享变量es?如果是这样的话,删除join
调用只是在主线程终止时通过杀死子线程来隐藏死锁。是的。所有工作线程共享一个主变量-映射的映射。我正在通过函数指针传递一个互斥锁,以锁定映射中元素的插入。但是,它现在是耳朵什么也不做,因为地图有时仍然返回错误的数据。除了点->程序经过这一点,所以地图已经填充,文件已经扫描了。只有在我调用terminateThreads()时它是否会无限期地挂起。因此,当我的线程此时都处于睡眠状态时,我看不到任何死锁。你不能在线程的主循环中使用bool,并希望在线程中观察到将其设置为true。需要使用std::atomic。这不是唯一的问题,工作线程可能会被阻塞,并且永远不会观察退出请求。一般来说,这是一个很难解决的问题,这是C++11添加的原因。
void ThreadPool::WorkerThread::thisWorkerThread()
{
_thisPool->synchronizeStartingThreads();
try
{
while (!_terminate)
{
{
_thisPool->_coutMtx.lock();
std::cout << std::this_thread::get_id() << " Sleeping..." << std::endl;
_thisPool->_coutMtx.unlock();
_busy = false;
std::unique_lock<std::mutex> lock(_threadMtx);
_threadCond.wait(lock);
}
_thisPool->_coutMtx.lock();
std::cout << std::this_thread::get_id() << " Awake..." << std::endl;
_thisPool->_coutMtx.unlock();
if(_terminate)
break;
_thisPool->_task(_pPath, *_pArgs, *_pMap, _thisPool->coutMatch_mtx, _thisPool->matchesMap_mtx);
_thisPool->_coutMtx.lock();
std::cout << std::this_thread::get_id() << " Finished Task..." << std::endl;
_thisPool->_coutMtx.unlock();
}
_thisPool->_coutMtx.lock();
std::cout << std::this_thread::get_id() << " Terminating" << std::endl;
_thisPool->_coutMtx.unlock();
}
catch (const std::exception&)
{
_ex = std::current_exception();
}
_isRunning = false;
}
void ThreadPool::terminateThreads()
{
for (std::vector<std::unique_ptr<WorkerThread>>::iterator it = _vThreads.begin(); it != _vThreads.end(); ++it)
{
it->get()->terminate();
//it->get()->_thisThread.detach();
// if thread threw an exception, rethrow it in main
if (it->get()->exception() != nullptr)
std::rethrow_exception(it->get()->exception());
}
}
// scans a path recursively for all files of selected extension type, calls thread to parse file
unsigned int Functions::Scan(wpath path, const Args& args, ThreadPool& pool)
{
wrecursive_directory_iterator d(path), e;
unsigned int filesFound = 0;
while ( d != e )
{
if (args.verbose())
std::wcout << L"Grepping: " << d->path().string() << std::endl;
for (Args::ext_T::const_iterator it = args.extension().cbegin(); it != args.extension().cend(); ++it)
{
if (extension(d->path()) == *it)
{
++filesFound;
pool.callThread(d->path());
}
}
++d;
}
std::cout << "Scan Function: Calling TerminateThreads() " << std::endl;
pool.terminateThreads();
std::cout << "Scan Function: Called TerminateThreads() " << std::endl;
return filesFound;
}
{
// terminate() is called
std::unique_lock<std::mutex> lock(_threadMtx);
// _threadCond.notify_all() is called here
_busy = false;
_threadCond.wait(lock);
// thread is blocked forever
}
_threadCond.wait_for(lock, std::chrono::milliseconds(30)); // hold the lock a max of 30ms
// after the lock, and the termination check
if(_busy)
{
Global::mFile_t rMap = _thisPool->_task(_pPath, *_pArgs, _thisPool->coutMatch_mtx);
_workerMap.element.insert(rMap.element.begin(), rMap.element.end());
}