繁体   English   中英

终止工作线程和主线程之间的竞争状态

[英]Race condition between terminating worker threads and main thread

我在从主线程终止工作线程时遇到问题。 到目前为止,我尝试的每种方法都会导致争用状况或死锁。

工作线程存储在名为ThreadPool的类的内部类中,ThreadPool使用unique_ptr维护这些WorkerThreads的向量。

这是我的ThreadPool的标题:

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:
};

到目前为止,我真正遇到的问题是从主线程调用TerminateThreads()时会导致死锁或竞争状态。

当我将_terminate标志设置为true时,在线程有机会唤醒并终止之前,main可能已经退出作用域并破坏了所有互斥对象。 实际上,我已经多次崩溃(控制台窗口显示:互斥锁在忙碌时被破坏)

如果我在notify_all()线程之后添加thread.join(),则线程有可能在连接发生之前终止,从而导致无限死锁,因为与终止线程的连接将无限期挂起程序。

如果我分离-与上述相同,但导致程序崩溃

如果我改为使用while(WorkerThread.isRunning())Sleep(0); 程序可能会崩溃,因为主线程可能在WorkerThread到达最后一个关闭括号之前退出。

在所有工作线程安全终止之前,我不确定还可以采取什么措施来停止主线程。 同样,即使在线程和main中使用try-catch,也不会捕获任何异常。 (我尝试过的所有方法都会导致程序崩溃)

在工作线程完成之前,我该怎么做才能停止主线程?

以下是主要功能的实现:

终止单个工作线程

void ThreadPool::WorkerThread::terminate()
{
    _terminate = true;
    _threadCond.notify_all();
    _thisThread.join();
}

实际的ThreadLoop

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());
    }
}

最后,正在调用线程池的函数(扫描函数在main上运行)

// 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;
}

我会再次重复这个问题:在工作线程完成之前,我该怎么做才能停止主线程?

我没有出现线程终止和连接的问题。

加入线程只不过是要等到给定线程终止后才行,所以这很简单。 如果线程已经完成执行, join将立即返回。

因此,您只想像在代码中已经做过的那样,在terminate调用期间加入每个线程。

注意:如果您刚刚终止的线程具有活动的exception_ptr则当前您会立即抛出任何exception_ptr 这可能导致未连接的线程。 处理这些异常时,您必须牢记这一点

更新:查看您的代码后,我看到一个潜在的错误:当发生虚假唤醒时, std::condition_variable::wait()可以返回。 如果是这种情况,您将在上次使用的路径上再次工作,从而导致错误的结果。 如果已经添加了新工作,则应该为新工作设置一个标志,并且_threadCond.wait(lock)行应处于检查标志和_terminate 不过,不确定是否可以解决您的问题。

问题有两个:

syncnizeStartingThreads()有时会阻塞1或2个线程,等待一切顺利(while(some_condition)barrierCond.wait(lock)中的问题。该条件有时永远不会评估为true。阻止问题。

第二个问题是工作线程有可能进入_threadMtx,并在进入_threadCond.wait()之前调用notify_all,因为已经调用了notify,所以线程将永远等待。

即。

{
    // 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
}

令人惊讶的是,将此互斥锁锁定在terminate()中并没有阻止这种情况的发生。

通过在_threadCond.wait()中添加30ms超时来解决此问题

另外,在任务开始之前添加了检查,以确保不会再次处理同一任务。

现在,新代码如下所示:

thisWorkerThread

_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());
        }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM