C : непонимание правил уничтожения объектов

#c #scope #destructor #casablanca

#c #область применения #деструктор #Касабланка

Вопрос:

В следующем фрагменте кода C используется пакет Microsoft C Rest SDK. Я не понимаю, почему первый фрагмент работает, а другие — нет. Я предполагаю, что различия связаны с уничтожением объектов и правилами определения области видимости. Я ищу объяснение того, почему первый фрагмент работает, а другие фрагменты зависают на close() . Кроме того, что может сделать SDK для устранения будущих ошибок. Некоторые действительно умные люди смотрели на фрагмент, но никогда не видели проблемы.

Первый фрагмент кода. Этот фрагмент работает и показан полностью. Последующие фрагменты кода заменяют помеченный код внутри. Пожалуйста, сосредоточьтесь на различиях, а не на других отвлекающих факторах. Код был протестирован путем создания одного запроса GET в браузере и пошагового выполнения кода. Во всех случаях request.reply() был выполнен один и только один раз.

     boost::lockfree::spsc_queue<web::http::http_request, boost::lockfree::capacity<1024>> queue;
web::http::experimental::listener::http_listener listener(U("http://localhost:3901"));
listener.support([amp;](web::http::http_request request)
{
    queue.push(request);
});
listener.open().wait();
std::cout << "listening ... hit enter to initiate shutdown." << std::endl;
std::getchar();
// BEGIN CODE IN QUESTION
while (!queue.empty())
{
    web::http::http_request request;
    if (queue.pop(request))
        request.reply(web::http::status_codes::ServiceUnavailable, U("Service Unavailable")).wait();
}
// END CODE IN QUESTION
listener.close().wait();
 

Второй фрагмент кода. Зависает при close() .

     // hangs on close().wait()
web::http::http_request request;
while (queue.try_pop(request))
{
    request.reply(web::http::status_codes::ServiceUnavailable, U("Service Unavailable")).wait();
}
 

Третий фрагмент кода. Зависает при close() .

     // hangs on close().wait(). Outer braces make no difference.
 {
    web::http::http_request request;
    while (queue.try_pop(request))
    {
        request.reply(web::http::status_codes::ServiceUnavailable, U("Service Unavailable")).wait();
        request.~http_request();
    }
}
 

Четвертый фрагмент кода. Зависает при close() . Внешние фигурные скобки не имеют значения.

     // hangs on close().wait()
{
    web::http::http_request request;
    while (queue.try_pop(request))
    {
        request.reply(web::http::status_codes::ServiceUnavailable, U("Service Unavailable")).wait();
        request.~http_request();
    }
    request.~http_request();
}
 

Обновление: поддерживая объяснение Мэтта Макнабба, следующий код работает, если я выдаю только один GET. Я просто удалил цикл для обработки одного GET. Явный вызов деструктора требуется, чтобы избежать зависания, но это неправильная практика.

     web::http::http_request request;
requests.pop(request);
request.reply(web::http::status_codes::ServiceUnavailable, U("Service Unavailable")).wait();
    request.~http_request();
 

Обновление: явный вызов деструктора после цикла заставляет программу работать для одного GET . Однако два или более GET генерируют исключение. Я не уверен, почему.

     web::http::http_request request;
while (queue.try_pop(request))
{
    request.reply(web::http::status_codes::ServiceUnavailable, U("Service Unavailable")).wait();
}
request.~http_request();
 

Комментарии:

1. Все строки request.~http_request(); вызывают неопределенное поведение и должны быть удалены. (Технически первый этого не делает, но затем UB появляется на втором или при завершении функции). Вызов деструктора дважды для одного и того же объекта или использование объекта после вызова деструктора является неопределенным поведением.

2. Разница между 1 и 2 заключается в том, что 1 каждый раз уничтожает старый запрос и создает новый; но 2 повторно использует один и тот же объект, не уничтожая и не создавая его на каждой итерации цикла. Было бы полезно, если бы вы опубликовали определение try_pop , чтобы мы могли видеть, какими средствами оно обновляет существующий объект. Также может быть, что web::http::http_request это не предназначено для поддержки любого try_pop используемого метода обновления.

3. Это может быть глупый вопрос, но что это try_pop такое? Это не отображается в документации по интерфейсу boost 1.55 для spsc_queue

4. повторно. ваше обновление с упоминанием моего имени, избавьтесь request.~http_request(); . Это вызывает неопределенное поведение. «Правила уничтожения объектов C » включают в себя, что вам разрешено вызывать деструктор только один раз.

5. Позвольте мне повторить это как можно более четко: если вы явно вызываете деструктор объекта в стеке, вы получите неопределенное поведение, поскольку деструктор будет дополнительно вызван во второй раз (автоматически) . Не делайте этого. Скорее сосредоточьтесь на том, почему второй пример не работает, и попытайтесь создать новый объект запроса на каждой итерации цикла, чтобы посмотреть, решит ли это проблему.

Ответ №1:

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

Простое решение:

  1. Объявите объект внутри блока, после чего он будет выделен.
  2. Не вызывайте деструктор явно (редко хорошая идея).
  3. Объект будет уничтожен при выходе из блока.

Казалось бы, его нужно уничтожить перед вызовом close(), что разумно.


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

Комментарии:

1. Разве второй пример не подразумевает, поскольку он вызывает зависание, что методы копирования или назначения объекта ответа не очищают целевой объект? Это то, что происходит не так?

2. Другой вопрос, недостаточно информации для размышлений. Для этого Q amp; A просто убедитесь, что вы уничтожаете объект ровно один раз, предпочтительно путем выхода блока, а не явного вызова dtor.

Ответ №2:

Отказ от ответственности: без каких-либо знаний об этой библиотеке это довольно опасно

 #include <iostream>
using namespace std;

class Obj {
public:
    Obj() {
        cout << "Constructor" << endl;
    }
    ~Obj() {
        cout << "Destructor" << endl;
    }
};

int main() {
    {
        Obj ea;
        ea.~Obj();
    }
    return 0;
}
 

Вывод:

 Constructor 
Destructor 
Destructor
 

Если для очистки запроса необходимо освободить ресурсы или выполнить операции, вы создаете много проблем в каждом случае, за исключением случаев, когда вы выполняете следующие шаги:

  1. Получение запроса путем копирования ( pop или try_pop )
  2. Обработка запроса
  3. Уничтожение и очистка запроса ОДИН РАЗ

В приведенных выше фрагментах:

  1. Объект копируется из очереди, обрабатывается и уничтожается. Каждый из них. Отлично.
  2. Объект копируется, обрабатывается, но не уничтожается. Зависает.
  3. Объект копируется, обрабатывается, уничтожается, а затем снова уничтожается. Зависает.
  4. Даже хуже, чем 3.
  5. Объект копируется, обрабатывается и уничтожается. Хорошо для слушателя, это может вызвать проблемы в конце области действия функции.
  6. Объект копируется, обрабатывается, и только один из них вызывает деструктор для очистки — таким образом, только один запрос. Штраф за один запрос.

Комментарии:

1. Что касается 2, да, объект запроса с обратной очередью не уничтожается. Программа зависнет, если объект ответа не выйдет за пределы области видимости.

2. Проблема, вызвавшая исходное сообщение, была решена в ветке разработки C Rest Sdk от 1 июля 2014 года.