std::thread, std::condition_variable и dll
От: Doom100500 Израиль  
Дата: 13.06.21 11:25
Оценка:
Привет эксперты.

Если бы не dll, то это не было бы в прикладных вопросах, но, если нет dll, то и проблема не воспроизводится.

Вобщем есть код в длл:

Woker.dll:

//worker.h

#ifdef WORKER_EXPORTS
#define WORKER_API __declspec(dllexport)
#else
#define WORKER_API __declspec(dllimport)
#endif

#include <string>
#include <memory>
#include <mutex>


struct WORKER_API WorkerBase {
    virtual ~WorkerBase() {};
    virtual void AddVal(const std::string& val) = 0;
};

class WorkerFactory {
public:
    WORKER_API static WorkerBase* instance();
private:
    static std::unique_ptr<WorkerBase> worker_ptr;
    static std::once_flag once;
};

//worker.cpp

#include "pch.h"
#include "framework.h"
#include "Worker.h"
#include <string>
#include <deque>
#include <mutex>
#include <thread>
#include <iostream>
#include <chrono>


class Worker : public WorkerBase {
    friend class WorkerFactory;
private:
public:
    Worker() {
        worker_thread = std::thread([this] {
            std::cout << "started working thread" << std::endl;
            while (is_running) {
                std::unique_lock<std::mutex> lk(queue_lock);
                queue_cv.wait(lk, [this] {return !queue.empty();});
                if (!is_running) {
                    std::cout << "break" << std::endl;
                }
                std::cout << "got val: " << queue.front() << std::endl;
                queue.pop_front();
            }
            std::cout << "exit working thread" << std::endl;
            });
    }

    ~Worker() {
        queue_lock.lock();
        is_running = false;
        queue.push_back("last value");
        queue_lock.unlock();
        queue_cv.notify_one();

        if (worker_thread.joinable())
            worker_thread.join();

        std::cout << "dtor completed" << std::endl;
    }


    void AddVal(const std::string& val) override {
        queue_lock.lock();
        queue.push_back(val);
        queue_lock.unlock();
        queue_cv.notify_one();
    }

private:
    std::deque<std::string> queue;
    std::mutex queue_lock;
    std::thread worker_thread;
    std::condition_variable queue_cv;
    std::atomic<bool> is_running = true;
};

std::unique_ptr<WorkerBase> WorkerFactory::worker_ptr;
std::once_flag WorkerFactory::once;

WorkerBase* WorkerFactory::instance() {
    std::call_once(once, [] {worker_ptr = std::unique_ptr<WorkerBase>(new Worker);});
    return worker_ptr.get();
}


и main. dll подключается через lib:


#include <chrono>
#include <thread>

#include "../Worker/Worker.h"


int main() {
    auto w = WorkerFactory::instance();
    std::this_thread::sleep_for(std::chrono::seconds(1));
    w->AddVal("1");
    std::this_thread::sleep_for(std::chrono::seconds(1));
    w->AddVal("2");
    std::this_thread::sleep_for(std::chrono::seconds(1));
    w->AddVal("3");

    std::this_thread::sleep_for(std::chrono::seconds(1));



    return 0;
}



output:

started working thread
got val: 1
got val: 2
got val: 3
dtor completed

Я всю голову сломал почему я не вижу в output "exit working thread" и "got val: last value", но join отработал без проблем ?
Если закоментировать join в деструкторе, то получаю исключение "abort called", что правильно.
Если закоментировать работу с conditional variable в деструцторе, то ничего не меняется, хотя поток не должен был завершиться.

Если это всё скомпилированно в одном модуле (exe), то всё работает правильно, т.е. получаю last value из десттруктора и вижу вывод "exit working thread".

Как так? Надо ли волноваться?

Если вдруг, кто-нибудь захочет поиграться, то вот solution
Спасибо за внимание
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.