Я изучаю синхронизацию потоков между двумя потоками, используя переменную условия, но получаю эту ошибку компиляции:
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;
}
Может кто-нибудь объяснить, что я делаю неправильно
будьте осторожны, вы не инициализируете opn_type
и не вызываете неопределенное поведение
Отвечает ли это на ваш вопрос? пересекает ошибку инициализации в операторе switch case
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 Ничего полезного, но это не его ошибка.
Переключатель считается вредным
Я думаю, что корень проблемы здесь в понимании сообщения об ошибке. Давайте разберем его:
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:
, и произойдет в закрывающей скобке.
Я не имею здесь ничего общего с примитивами потоковой передачи - этот ответ должен быть общим.
Защита блокировки определена для всего тела
switch
, но инициализируется только в одной ветке. Поместите код, который его использует, в фигурные скобки.