2015-04-11 3 views
2

У меня есть класс, который вызывает асинхронную задачу, используя std::async в своем конструкторе для загрузки его содержимого. (Я хочу загрузку объекта выполняется асинхронно)Деструкторы и асинхронные задачи

код выглядит следующим образом:

void loadObject(Object* object) 
{ 
// ... load object 
} 

Object::Object(): 
{ 
    auto future = std::async(std::launch::async, loadObject, this); 
} 

Я несколько экземпляров этих объектов получение создаются и удаляются на моем главном потоке, они могут получить удалены в любое время , еще до того, как их загрузка закончилась.

Я хотел бы знать, опасно ли уничтожить объект, когда он все еще обрабатывается в другом потоке. И как я могу остановить поток, если объект будет уничтожен?

EDIT:std::future деструктор не блокирует мой код с компилятором VS2013, что я использую из-за bug.

+0

Неопределенное поведение для изменения объекта и доступа к нему в другом потоке одновременно без синхронизации. Что вы ожидаете, должно произойти, когда объект, с которым вы работаете, удаляется из-под ног? –

+0

_ «они могут быть удалены в любое время, даже до того, как их загрузка закончится». _ No no no no no! –

+0

@LightningRacisinObrit, это ничего плохого в том, чтобы удалить smth, который еще не загружен. Вам просто нужно загрузить его атомарно (BTW, именно вы разрабатываете современные браузеры, которые никогда не перестанут загружать страницу посередине?) – Lol4t0

ответ

3

Как MikeMB уже упоминалось, ваш конструктор не заканчивается, пока нагрузка не имеет были завершены. Проверьте этот вопрос о том, как преодолеть это: Can I use std::async without waiting for the future limitation?

я хотел бы знать, если это опасно, имеющий объект получения разрушен, когда он по-прежнему получать обрабатываются в другом потоке.

Доступ к памяти объекта после удаления, безусловно, опасен, да. Поведение будет неопределенным.

Как остановить поток, если объект будет уничтожен?

Что я рекомендую вам, чтобы заботиться о первых, чтобы убедиться, что объект не разрушаться, пока он еще указал на что-то, что собирается использовать его.

Один из подходов - использовать флаг участника, означающий заполненную нагрузку, которая обновляется в задаче async и проверяется в деструкторе и синхронизирует доступ с переменной условия. Это позволит деструктору блокироваться до завершения асинхронной задачи.

Как только вам удалось предотвратить уничтожение объекта, вы можете использовать другой синхронизированный флаг участника, чтобы обозначить, что объект уничтожается и пропускать загрузку, если она установлена. Это добавит накладные расходы синхронизации, но может стоить того, если загрузка будет дорогостоящей.

Другой подход, который позволяет избежать блокировки деструктора, - передать std::shared_ptr в задачу async и потребовать, чтобы все экземпляры Object принадлежали общему указателю. Это ограничение может быть не очень желательным, и вам нужно наследовать std::enable_shared_from_this, чтобы получить общий указатель в конструкторе.

3

В вашем коде нет ничего асинхронного, потому что блок-конструктор блокируется до тех пор, пока loadObject() не вернется (деструктор будущего, возвращаемый std::async, неявно соединяется).

Если бы это было не так, это зависело бы от того, как вы написали свой код (и особенно ваш деструктор), но, скорее всего, ваш код подвергнется неопределенному поведению.

+0

Я уже слышал об этом деструкторе, я сомневался, но я проверил свою программу и, похоже, действительно асинхронно. У меня есть элемент «update()» на моем объекте, который вызывается каждый кадр и сообщает мне, загружены ли объекты ... и я вижу, что он печатает, что он не в течение нескольких кадров после построения. – thp9

+0

_ "но я протестировал свою программу и, похоже, действительно асинхронный" _ Тогда вы протестировали ее неправильно. –

+0

Тогда, я думаю, я до сих пор не знаю, что такое асинхронные средства. Как вы можете объяснить, что код пост-конструктора выполняется, пока объект еще не загружен, если конструктор блокирует? – thp9

-1

Да опасно имея объект получения разрушен, когда он по-прежнему получать обрабатываются в другом потоке

Вы можете реализовать много стратегий на самом деле в зависимости от потребностей и желаемого поведения.

Я бы применил здесь стратегию pimpl, это означает, что все фактические данные будут храниться в указателе, который хранится в вашем объекте. Вы загрузите все данные в объект-указатель данных и сохраните его в общедоступном объекте атомарно.

Объект, говорящий по-английски, должен быть полностью сконструирован и готов к использованию к моменту завершения конструктора. В вашем случае объект-указатель данных, вероятно, еще не будет готов к использованию. И вы должны заставить свой класс правильно обрабатывать это состояние.

Так вот мы идем:

class Object 
{ 
    std::shared_ptr<Object_data> d; 
    Object::Object(): 
     d(std::make_shared<Object_data>()) 
    { 
     some_futures_matser.add_future(std::async(std::launch::async, loadObject, d)); 

    } 
} 

Затем сделать атомный флаг в данном-объекте, который будет сигнализировать, что загрузка завершена и объект готов к использованию.

class Object_data 
{ 
    // ... 
    std::atomic<bool> loaded {false}; 
}; 

loadObject(std::shared_ptr<Object_data> d) 
{ 
    /// some load code here 
    d->loaded = true; 
} 

Вы должны проверить, если ваш объект constrcuted каждый раз, когда вы Acces его (с резьбой безопасным способом) через loaded флаг

+2

Вы ничего не сделали, чтобы безопасно уничтожить объект, пока он используется в другом месте (что невозможно , как я уже говорил). Все, что вы сделали, это добавить _block_ в _defer_ уничтожение объекта, пока оно не будет использоваться в другом месте. –

+0

"some_futures_matser" - что это? – sehe

+1

«some_futures_matser» Просто, чтобы не автосоединить – Lol4t0

Смежные вопросы