簡體   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