Переход к метке case пересекает инициализацию 'std::unique_lock<std::mutex>'

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

CVV.cpp:21:3: error: jump to case label [-fpermissive]
   default:
   ^
CVV.cpp:14:33: error:   crosses initialization of ‘std::unique_lock<std::mutex> ul’
    std::unique_lock<std::mutex> ul(m, std::defer_lock);

это мой код

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex m;
std::condition_variable cv;

void recv_data() {
    int32_t opn_type;
    
    switch(opn_type) {
        case 1:
            std::unique_lock<std::mutex> ul(m);
            // do processing and add to queue
            cv.notify_one();
            ul.unlock();
            
            break;
            
        default:
            break;
    }
}

int32_t main() {
    std::thread th(recv_data);
    th.join();
    
    return EXIT_SUCCESS;
}

Может кто-нибудь объяснить, что я делаю неправильно

Защита блокировки определена для всего тела switch, но инициализируется только в одной ветке. Поместите код, который его использует, в фигурные скобки.

bipll 18.12.2020 17:49

будьте осторожны, вы не инициализируете opn_type и не вызываете неопределенное поведение

463035818_is_not_a_number 18.12.2020 17:52

Отвечает ли это на ваш вопрос? пересекает ошибку инициализации в операторе switch case

underscore_d 18.12.2020 18:27
Почему в Python есть оператор &quot;pass&quot;?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Travel Booking Angular Template один из лучших Travel & Tour booking template in the world. 30+ валидированных HTML5 страниц, которые помогут...
2
3
575
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий
switch(opn_type) {
    case 1: {
        std::unique_lock<std::mutex> ul(m);
        // do processing and add to queue
        cv.notify_one();
        ul.unlock();
        
    }   break;
        
    default:
        break;
}

метки case не заканчивают время жизни переменной, поэтому ul существует и уничтожается после default:, но инициализируется только в case 1:.

Поместите его в блок.

switch(opn_type) {
  case 1: {
    std::unique_lock<std::mutex> ul(m);
    // do processing and add to queue
    cv.notify_one();
  }   break;
    
  default:
    break;
}

.unlock() ничего не делает, так как область действия заканчивается прямо здесь. В этой версии я его удалил.

Обратите внимание, что я считаю смешивание примитивов многопоточности с другим кодом опасным.

template<class T>
struct threadsafe_queue {
  T pop() {
    auto l = lock();
    cv.wait(lk, [&]{return !queue.empty();});
    T retval = queue.front();
    queue.pop_front();
    return retval;
  }
  void push( T in ) {
    auto l = lock(m);
    queue.push_back(std::move(in));
    cv.notify_one();
  }
  std::deque<T> pop_all() {
    auto l = lock();
    return std::move(queue);
  }
private:
  mutable std::mutex m;
  std::condition_variable cv;
  std::deque<T> queue;
  std::unique_lock<std::mutex> lock() const {
    return std::unique_lock<std::mutex>(m);
  }
};

добавление «try_pop», «try_pop_for» и т. д. — это упражнение.

@TedLyngmo Ничего полезного, но это не его ошибка.

Yakk - Adam Nevraumont 18.12.2020 17:49

Переключатель считается вредным

463035818_is_not_a_number 18.12.2020 17:53

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

CVV.cpp:21:3: error: jump to case label [-fpermissive]
   default:
   ^
CVV.cpp:14:33: error:   crosses initialization of ‘std::unique_lock<std::mutex> ul’
    std::unique_lock<std::mutex> ul(m, std::defer_lock);

Обратите внимание, что второе сообщение об ошибке имеет отступ: слово crosses находится дальше, чем слово jump. Это не имеет смысла, хотя, надо признать, этот смысл может быть и не сверхочевидным. Отступ подразумевает продолжение сообщения, т.е. вы должны читать его вместе с предыдущими сообщениями, начиная с ближайшего сообщения без отступа. Компилятор указывает, что ошибка распространяется по нескольким связанным местам. Это не две ошибки, а одна ошибка, которая дает столь необходимые детали. Сообщение должно быть прочитано непрерывно. Таким образом, это не ошибка "перекрестной инициализации". Это ошибка «переход к метке случая пересекает инициализацию». Теперь это начинает иметь какой-то смысл, я надеюсь :)

Вот как это читать:

В CVV.cpp, строка 21, есть ошибка, вызванная переходом на метку case default:,
потому что этот переход пересекает инициализацию ul, сделанную в строке 14.

Напомним, что switch действует примерно так:

if (argument == case1) goto case1;
else if (argument == case2) goto case2;
...
else goto default;

Итак, поскольку switch должен перейти от switch к метке default:, он пересекает инициализацию. Так что все, что вам нужно, чтобы исправить ошибку, это избавиться от инициализации :)

Но «избавление от этого» не обязательно должно быть буквальным. Проблема, которую вы пытаетесь решить, заключается в том, что ul виден на метке default, но когда вы переходите к этой метке, инициализация для ul не выполняется, так как вы перепрыгиваете через нее, но ul находится в области видимости, т.е. доступна для любого код в случае default:. Таким образом, этот код потенциально может использовать неинициализированный объект. Но это только половина проблемы. Несуществующий объект ul может быть уничтожен только тогда, когда он больше не находится в области видимости, а в разделе default: он все еще находится в области видимости. Таким образом, разрушение будет выполняться впоследствии, без построения, и это тоже неправильное и неопределенное поведение.

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

switch(opn_type) {
    default:
        break;
    case 1:
        std::unique_lock<std::mutex> ul(m);
        // do processing and add to queue
        cv.notify_one();
        ul.unlock();            
        break;            
}

Это не всегда возможно, конечно. Это просто пример, чтобы было понятно, что самое "очевидное" решение тоже сработало бы.

Таким образом, более общее решение состоит в том, чтобы вставить дополнительную область действия, заключив код в блоки:

switch(opn_type) {
    case 1: {
        std::unique_lock<std::mutex> ul(m);
        // do processing and add to queue
        cv.notify_one();
        ul.unlock();            
        break;
    }            
    default:
        break;
}

В этом случае переход к default: допустим, так как в этот момент ul находится вне области видимости, т. е. невидимо, и, таким образом, не будет возможности получить доступ к неинициализированному объекту, а также попытаться уничтожить и объект, который не существуют (синоним «не построен»). Разрушение произойдет только тогда, когда сначала будет введен блок case 1:, и произойдет в закрывающей скобке.

Я не имею здесь ничего общего с примитивами потоковой передачи - этот ответ должен быть общим.

Другие вопросы по теме