开发者

What if I don't join thread on "destruction" in release builds?

开发者 https://www.devze.com 2023-01-15 02:35 出处:网络
In many cases I have classes that act like active objects (have a thread). And to avoid access violations I always have to wait for join in the destructor. Which is usually not a problem.

In many cases I have classes that act like active objects (have a thread). And to avoid access violations I always have to wait for join in the destructor. Which is usually not a problem.

However imagine a release build with some bug (deadlock, livelock etc.) that causes join() not to return on time or at all, this would cause the entire application to become non functional while waiting for an object that will not be used again. If this happens at a customer it becomes a problem.

I would rather be notified by the problem and skip the join. And leak the thread and it's resources.

Skipping the join could be achieved through something like this.

   class MyActiveObject
    {
    public:
        MyActiveObject();
        ~MyActiveObject(){}
    private
        struct Implementation;
        std::shared_ptr<Implementation> pImpl_;
    };

    struct MyActiveObject::Implementation : std::enable_shared_from_this<Implementation >
    {
        Implementation() : thread_([=]{Run();})
        {
        }

        ~Implementation()
        {
            #ifdef _DEBUG
                thread_.join();
            #else
开发者_如何学编程                if(!thread_.timed_join(SOME_TIMEOUT))
                    ALERT_SOME_HOW();
            #endif
        }

        void Dispose()
        {
            isRunning_ = false;
        }

        void Run()
        {
        #ifndef _DEBUG
            auto pKeepAlive = shared_from_this(); // Won't be destroyed until thread finishes
        #endif   
            isRunning_ = true;
            while(isRunning_)
            {
                 /* ... */
            }
        }

        boost::thread thread_;
        tbb::atomic<bool> isRunning_;
    };

MyActiveObject::MyActiveObject() : pImpl_(new Implementation()){}
MyActiveObject::~MyActiveObject() { pImpl_->Dispose(); }

Is this a good idea? Or are there better strategies?


Odd question: 'I have a problem if I have a bug in my code'. Well, yes. Fix the bug, don't try to paper it over. That kind of paper only ever produces two bugs because you can't test it until you know what the first bug is.


It would be better, if feasible, to activate some kind of structure analysis to find the cycle and break the deadlock.

It might miss some deadlocks on destruction, but then again it might also be applicable to catch deadlocks outside that context.


If you are writing a client application and the lifetime of the application is short after this destruction sequence then it seems a reasonable pragmatic solution. I would encourage you to add some logging and to attempt to collect release logs (at least from your internal testing).

If you are writing a server application and this destruction is not during the shutdown of the server then I would discourage this behaviour as your server frequently running out of resources is unlikely to be good.

In both cases any known deadlock issues should be chased with a high priority by the development team - even if the effect is hidden from the customer.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号