nofify_all () вылетает при закрытии программы - PullRequest
0 голосов
/ 28 февраля 2019

У меня очень простая программа на C ++, как показано ниже.Классы A, B и C находятся внутри DLL.Когда я закрываю это приложение, оно иногда падает, вызывая notify_all () для условной переменной.Кто-нибудь может дать мне подсказку о причине?

Я проверил много вопросов и ответов на SO, но ни один из них не решил мою проблему.Я работаю на Windows 7 и VS2013.

    class B;

    class C
    {
    public:
        C(const std::weak_ptr<B>& b) : mB(b)
        {
        }
        virtual ~C() 
        {
        }

        void run()
        {
            while (true)
            {
                std::unique_lock<std::mutex> uLock(mMutex);

                // Wait until some event is happening
                mCondition.wait_for(uLock, std::chrono::seconds(300));

                if (!mStop)
                {
                    //do something here
                }
                else
                {
                    return;
                }
            }
        }


        void start()
        {
            mThread = std::thread(&C::run, this);
        }

        void stop()
        {
            mStop = false;
        }

        void notify()
        {
            mCondition.notify_all();
        }

        void join()
        {
            if (mThread.joinable())
            {
                mThread.join();
            }
        }

        private:
            std::atomic<bool> mStop;
            std::condition_variable mCondition;
            std::mutex mMutex;
            std::thread mThread;
            std::weak_ptr<B> mB;
        };


    class B : public std::enable_shared_from_this<B>
    {
    public:
        B() {}
        ~B()
        {
            if (mC)
            {
                mC->stop();
                mC->notify();
                mC->join();
            }
        }

        // basic methods
        void init()
        {
            mC = std::unique_ptr<C>(new C(shared_from_this()));
            mC->start();
        }

    private:
        std::unique_ptr<C> mC;
    };

    class A
    {
    public:
        ~A(){}

        void init() { pImpl->init(); }

        static std::shared_ptr<A> getInstance(){
            static std::shared_ptr<A> instance(new A);
            return instance;
        }

    private:
        A() : pImpl(std::make_shared<B>()){}
        std::shared_ptr<B> pImpl;
    };


    void main()
    {
        std::shared_ptr<A> a = A::getInstance();
        a->init();

        int x;
        std::cin >> x;
    }

Edit 1: Если я помещаю код в деструктор B в другую функцию (например, clean ()) и вызываю его из main () (используя метод clean () в A) не происходит сбоев.

Ответы [ 2 ]

0 голосов
/ 03 марта 2019

Похоже, что это ошибка CRT (https://stackoverflow.com/a/50525968/896012). Проблема не возникает в новых версиях Windows, то есть Windows 10. Чтобы исправить сбой в Windows 7, я просто удалил condition_variable и использовал простой сонвместо этого и при выходе из программы я просто отключил поток. Хотя это не чистый подход, но я думаю, что это единственный способ избежать сбоя. Если кто-то нашел лучший ответ, просто дайте мне знать.

0 голосов
/ 28 февраля 2019

Код пропускает уведомление переменной условия, потому что:

  1. Мьютекс не удерживается в течение stop_ = true (это должно быть true, а не false).stop_ необходимо прочитать и изменить, пока удерживается мьютекс, и он не должен быть атомарным.Это частая причина состояний гонки, когда люди используют атомики вместе с мьютексами и переменными условия.
  2. Код ожидания переменной условия не проверяет условие перед ожиданием.

Исправления:

class B;

class C
{
public:
    C(const std::weak_ptr<B>& b) : mB(b) {}
    ~C() { stop(); }

    void run()
    {
        while (true) {
            std::unique_lock<std::mutex> uLock(mMutex);
            while(!mStop /* && !other_real_condition */)
                mCondition.wait_for(uLock, std::chrono::seconds(300));
            if(mStop)
                return;
            // other_real_condition is true, process it.
        }
    }


    void start()
    {
        mThread = std::thread(&C::run, this);
    }

    void stop()
    {
        {
            std::unique_lock<std::mutex> uLock(mMutex);
            mStop = true;
        }
        mCondition.notify_all();
        if (mThread.joinable())
            mThread.join();
    }

    private:
        bool mStop = false; // <--- do not forget to initialize
        std::condition_variable mCondition;
        std::mutex mMutex;
        std::thread mThread;
        std::weak_ptr<B> mB;
    };


class B : public std::enable_shared_from_this<B>
{
public:

    // basic methods
    void init()
    {
        mC = std::unique_ptr<C>(new C(shared_from_this()));
        mC->start();
    }

private:
    std::unique_ptr<C> mC;
};

Если вы установили mStop без удержания мьютекса, произойдет следующее:

| Thread 1              | Thread 2            |
| mStop = true          |                     |
| mCondition.notify_all |                     |
|                       | mMutex.lock         |
|                       | mCondition.wait_for |

В приведенном выше обсуждении поток 2 теряет уведомление и ожидает, хотя mStop было установлено.

Блокировка мьютекса при обновлении общего состояния исправляет это состояние гонки:

| Thread 1                | Thread 2               |
| mMutex.lock             |                        |
| mStop = true            |                        |
| mCondition.notify_all   |                        |
| mMutex.unlock           |                        |
|                         | mMutex.lock            |
|                         | mStop == true, no wait |

При ожидании переменных состояния общее состояние должно быть изменено и прочитано, пока удерживается блокировка мьютекса, в противном случае уведомления об условияхпотеряться, и это может привести к тупику (при ожидании без таймаута).Вот почему использование атомики вместе с мьютексами и условными переменными не нужно, вы либо используете атомики или мьютексы и условные переменные, но не оба.

...