在新线程中构造对象?

时间:2013-09-07 10:40:56

标签: c++ c++11 architecture stdthread

我正在尝试构建对象,需要一段时间来构建一个单独的线程。 (稍后在实时加载游戏资产时,渲染循环正在运行:))

当对象仍在构建时,对该对象的请求不会失败,而是替换它。 (就像没有纹理的绘图一样,而纹理仍在加载,在游戏中)。 我的方法是使用State-Pattern - 一个状态可用于静止加载。 (我的第一个是Proxy,但你真的不想看那个代码!)

以下是完整的源代码:

#include <thread>
#include <iostream>
#include <list>
using namespace std;


class Object
{
private:
    int _data;
    /// Classes for states ///
    class IState
    {
    public:
        virtual void Use(Object *obj) = 0;
    };
    class Unavailable : public IState
    {
    public:
        void Use(Object *obj)
        {cout << "Object not avaiable..." << endl;}
    };
    class Avaiable : public IState
    {
    public:
        void Use(Object *obj)
        {cout << "Data is: " << obj->_data << endl;}
    };
    ////////////////////////
    IState *_state;
    void ChangeState(IState *newstate)
    {
        delete _state;
        _state = newstate;
    }
    void Construct() //Can this be part of IState?
    {
        this_thread::sleep_for(chrono::seconds(1)); //Work hard
        _data = 50;
        ChangeState(new Avaiable());
    }
public:
    Object()
    {
        //Set the state to unavaiable
        _state = new Unavailable();
        //Construct the object in seperate thread
        thread constructor(&Object::Construct, this); //How do I refer to Construct if its a member of IState?
        constructor.detach();
        //Thread runs while out of scope!
    } 
    ~Object()
    {delete _state;}

    void Use()
    {
        //Redirect actions
        _state->Use(this);
    }
};


int main()
{
    {
        list<Object*> objects;
        for (int i = 0; i < 10; i++)
        {
            this_thread::sleep_for(chrono::milliseconds(500));
            objects.push_back(new Object()); //I can't use Object as the list type, because of push_back()
                                             //copying the Object then DELETING it, thus deleting the state
            for (auto obj : objects) //The objects, which are already build shoud write "50"
                                     //otherwise it should write "Not avaiable" as a replacement
            {
                obj->Use();
            }
        }
        //Free the objects (I really want to avoid this....)
        for (auto obj : objects)
            delete obj;
    } //Extra scope to prevent false memory leaks!
    _CrtDumpMemoryLeaks(); //Reports memory leak somewhere. Couldn't track the block back to my code(...)
}

问题是(如代码中所述):

  1. 我如何在Construct()界面中移动IState方法以改进设计(如果对象已经可用,则放弃Construct()调用!)
  2. 我可以列出Object而不是Object*吗?
  3. 那个内存泄漏是什么? (我无法使用_CrtSetBreakAlloc跟踪我自己的代码块?!)
  4. 寻找您对设计的答案和评论。 (我刚刚开始处理软件架构,所以如果这种方法是垃圾,请纠正我;))

1 个答案:

答案 0 :(得分:1)

这不是线程安全的,因为如果你调用Object::Use并且在它正在使用时,另一个线程中的构造完成,它会删除仍然在主线程中使用的虚拟对象,从而导致未定义的行为

我现在看到两种方法:

1 而不是普通指针,请使用shared_ptr

class Object
{
private:
    int _data;
    std::shared_ptr<IState> _state;
    void ChangeState(std::shared_ptr<IState> newstate) {
      std::atomic_exchange(&_state, std::move(newstate));
    }
    void Construct() {
      //...
      ChangeState(std::make_shared<Avaiable>());
    }
public:
    Object() : 
      _state{std::make_shared<Unavailable>()}
    {
      std::thread constructor(&Object::Construct, this); 
      constructor.detach();
    } 
    ~Object()
    {}

    void Use() {
      std::shared_ptr<IState> currentState = std::atomic_load(&_state); //!!
      currentState->Use(*this)
    }
};

状态的atomic_load很重要,它会复制当前shared_ptr并且整个Use操作在旧状态下执行,即使它同时被交换。然后在Use()完成执行后,在主线程中完成销毁。

2使用未来并主动查找构造的对象:

class Object
{
private:
    int _data;
    typedef std::unique_ptr<IState> StatePtr;
    typedef std::future<StatePtr()> Constructor; 

    StatePtr _state;
    Constructor _constructor

    StatePtr  Construct() {
      //...
      return std::make_unique<Avaiable>();
    }

    static bool isReady(Constructor& ctor) {
      return ctor.valid()
        && ctor.wait_for(std::chrono::seconds(0)) != std::future_status::timeout;
    }

    IState& getState() {
      if (isReady(_constructor)) {
        _state = _constructor.get();
        _constructor.reset();
      }
      return *_state;
    }


public:
    Object() : 
      _state{std::make_unique<Unavailable>()} ,
      _constructor{ std::async(std::launch::async, [this](){Construct();} )}
    {} 

    void Use() {
      getState().Use(*this);
    }
};