Разблокировка Mutex не удалась странно

Я играю с некоторыми сокетами, потоками и мьютексами. Мой вопрос касается потоков и мьютексов:

int ConnectionHandler::addNewSocket(){

    this->connectionList_mutex.lock();
    std::cout << "test1" << std::endl;
    this->connectionList_mutex.unlock();

    return 0;
}

int ConnectionHandler::main(){
    while(true){
        this->connectionList_mutex.lock();
        std::cout << "test2" << std::endl;
        this->connectionList_mutex.unlock();
    }

}`

Основная функция работает в одном потоке, а addNewSocket вызывается другим потоком. Проблема заключается в том, что когда addNewSocket вызывается один раз (по второму потоку), следующая разблокировка по потоку 1 (main) завершится неудачно со странным «сигналом SIGABRT». Я уже два дня работал над этим, но, к сожалению, мне это не удалось. Я надеюсь, что вы можете мне помочь.

Изменить: ConnectionHandler - это класс, который имеет connectionList_mutex как член.

Edit: Иногда я также получаю эту ошибку: «Assertion failed: (ec == 0), разблокировка функции, файл /SourceCache/libcxx/libcxx-65.1/src/mutex.cpp, строка 44." но происходит случайным образом.

Изменить: это весь класс (уменьшенный до минимума, должен быть независимым от контекста в определенной степени, но сбой, когда я устанавливаю его сразу после подключения клиента и работает, если я правильно его установлю сразу после запуска:

class ConnectionHandler{
public:
    ConnectionHandler();
    int addNewSocket();
private:
    int main();
    static void start(void * pThis);

    std::mutex connectionList_mutex;
};

ConnectionHandler::ConnectionHandler(){
    std::thread t(&this->start, this);
    t.detach();
}
void ConnectionHandler::start(void * pThis){
    ConnectionHandler *handlerThis;
    handlerThis = (ConnectionHandler *)pThis;
    handlerThis->main();
}


int ConnectionHandler::addNewSocket(){

    this->connectionList_mutex.lock();
    std::cout << "test1" << std::endl;
    this->connectionList_mutex.unlock();

    return 0;
}

int ConnectionHandler::main(){
    while(true){
        this->connectionList_mutex.lock();
        std::cout << "test2" << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        this->connectionList_mutex.unlock();

    }

}
6
nl ja de
да мьютекс и поток оба c ++ 11 std
добавлено автор sh4kesbeer, источник
и это происходит, хотя ни создание, ни вызов функции не принимают параметры, поэтому они должны быть независимыми от контекста или?
добавлено автор sh4kesbeer, источник
хорошо, поэтому я сделал некоторый прогресс, но он стал более странным ... Когда я создаю ConnectionHandler и вызываю addNewSocket в (?), это зависит от того, где я поставил создание и вызов, поэтому, когда я делаю это сразу после выполнения началось, все будет работать, но когда я вложил его в контекст, когда клиент подключил его, это не сработает ...
добавлено автор sh4kesbeer, источник
хорошо, может быть, я глуп, но теперь (я создал небольшой код), который работает, мне просто нужно выяснить, почему он не работает в контексте моей реальной программы
добавлено автор sh4kesbeer, источник
хорошо, я попытаюсь использовать их, надеюсь, они переносимы, потому что я кодирую и отлаживаю Mac OS и хочу развернуть на Linux, спасибо за ваш ответ!
добавлено автор sh4kesbeer, источник
Почему тег std? Ваш мьютекс std :: mutex или что-то еще?
добавлено автор Manoj R, источник
Вероятно, это не поможет с вашей проблемой, но следует рассмотреть возможность использования оболочек RAII ( lock_guard или unique_lock )), чтобы блокировать мьютексы, а не блокировать и разблокировать их вручную. Таким образом, он не останется заблокированным навсегда, если блок выйдет раньше или выбрасывает исключение.
добавлено автор Mike Seymour, источник
@ sh4kesbeer: они стандартные классы C ++ 11, поэтому они должны быть переносимыми в любую точку, где вы можете использовать std :: mutex .
добавлено автор Mike Seymour, источник
Один из немногих раз, когда я видел :: std :: endl , когда это действительно нужно было.
добавлено автор Omnifarious, источник
@jstine: Да, учитывая тот факт, что он создает свой собственный отдельный поток, который не имеет возможности отключиться, почти наверняка будет уничтожен этот объект (и связанный с ним мьютекс) до выхода потока, поэтому я дал ответ я сделал.
добавлено автор Omnifarious, источник
«Assertion failed: (ec == 0)» - это может произойти только в двух случаях. 1. Мьютекс разрушен. 2. память повреждена. Я подтвердил это, посмотрев публичный src для mutex.cpp (который просто вызывает pthread_mutex_unlock и утверждает результат)
добавлено автор jstine, источник

1 ответы

Я предполагаю, что ваш объект ConnectionHandler где-то уничтожается. Кроме того, вы указали ConnectionHandler :: start глупо.

Во-первых, ConnectionHandler :: start должен быть определен следующим образом:

void ConnectionHandler::start(ConnectionHandler * pThis){
    pThis->main();
}

Класс C ++ 11 :: std :: thread отлично способен сохранить тип аргумента функции, поэтому нет необходимости прибегать к void * .

Во-вторых, добавьте этот код:

void ConnectionHandler::~ConnectionHandler(){
    const void * const meptr = this;
    this->connectionList_mutex.lock();
    ::std::cout << "ConnectionHandler being destroyed at " << meptr << ::std::endl;
    this->connectionList_mutex.unlock();
}

И измените конструктор следующим образом:

ConnectionHandler::ConnectionHandler(){
    const void * const meptr = this;
    ::std::cout << "ConnectionHandler being created at " << meptr << ::std::endl;
    std::thread t(&this->start, this);
    t.detach();
}

Это покажет вам, когда объект ConnectionHandler будет уничтожен. И я предполагаю, что ваш код уничтожает его, пока ваш выделенный поток все еще работает.

The meptr thing is because operator << has an overload for void * that prints out the pointer value. Printing out the pointer value for this will allow you to match up calls to the constructor and destructor if you're creating multiple ConnectionHandler objects.

Edit: Since it turned out I was correct, here is how I would recommend you write the play ConnectionHandler class:

#include 
#include 
#include <thread>
#include 
#include 

class ConnectionHandler {
 public:
   ConnectionHandler();
   ~ConnectionHandler();
   ConnectionHandler(const ConnectionHandler &) = delete;
   const ConnectionHandler &operator =(const ConnectionHandler &) = delete;

   int addNewSocket();

 private:
   int main();
   static void start(ConnectionHandler * pThis);

   ::std::mutex connectionList_mutex;
   volatile ::std::atomic_bool thread_shutdown;
   ::std::thread thread;
};

ConnectionHandler::ConnectionHandler()
     : thread_shutdown(false), thread(&this->start, this)
{
}

ConnectionHandler::~ConnectionHandler()
{
   thread_shutdown.store(true);
   thread.join();
}

void ConnectionHandler::start(ConnectionHandler * pThis){
   pThis->main();
}

int ConnectionHandler::addNewSocket(){
   ::std::lock_guard< ::std::mutex> lock(connectionList_mutex);
   ::std::cout << "test1" << ::std::endl;

   return 0;
}

int ConnectionHandler::main(){
   while(!thread_shutdown.load()){
      ::std::lock_guard< ::std::mutex> lock(connectionList_mutex);
      ::std::cout << "test2" << ::std::endl;
      ::std::this_thread::sleep_for(::std::chrono::milliseconds(100));

   }
   return 0;
}
5
добавлено
Хорошо, я нашел ошибку: я создал ConnectionHandler внутри функции, и как только функция завершилась, экземпляр был уничтожен. В связи с этим ссылкой я выяснил, что мне нужно создать указатель, который указывает на обработчик, созданный с помощью «нового» оператора (ConnectionHandler * newHandler = new ConnectionHandler;). В этом случае (как unsterstood) указатель уничтожается, но экземпляр ConnectionHandler остается в памяти. Снова: Спасибо за вашу любезную помощь!
добавлено автор sh4kesbeer, источник
Хорошо, я получил его сейчас, спасибо за вашу помощь, снова!
добавлено автор sh4kesbeer, источник
указатель на себя (ConnectionHandler) в качестве параметра), поэтому поток ConnectionHandler переходит в область действия Acceptor, и оттуда ConnectionHandler уничтожается.
добавлено автор sh4kesbeer, источник
Хорошо, я попытаюсь объяснить это лучше :). У меня есть Акцептор с основной функцией и ConnectionHandler с основной функцией. Каждый из них имеет собственный поток, который выполняет свою основную функцию. Когда клиент подключается (и нет ConnectionHandler), Acceptor создаст ConnectionHandler и передаст соединение. Теперь ConnectionHandler заботится о соединении. Как только он замечает, что клиент отключен, основной цикл ConnectionHandler будет прерван, и ConnectionHandler-поток уведомит Acceptor (посредством функции-члена Acceptor с
добавлено автор sh4kesbeer, источник
Я должен добавить sth. здесь: ConnectionHandler не уничтожается другим потоком, а просто выходит из области родительского Acceptor (поток ConnectionHandler обрабатывает ConnectionHandlers main (), и, заметив, что нет подключений, он вызывает функцию на Acceptor, в которой ConnectionHandler уничтожен)
добавлено автор sh4kesbeer, источник
Хорошо, я отменил большинство из них, но что делают эти две строки: ConnectionHandler (const ConnectionHandler &) = delete; const ConnectionHandler & operator = (const ConnectionHandler &) = удалить; ?
добавлено автор sh4kesbeer, источник
Я не понимаю, что вы имеете в виду, в контексте реального проекта (не это минимальное решение) ConnectionHandler создается и принадлежит Согласителю (Объекту), который обрабатывает входящие соединения и отключает. План (на данный момент) заключается в том, что этот Акцептор будет уничтожать и создавать ConnectionHandlers динамически по мере необходимости. Я надеюсь, что это сработает, поскольку доступ к данным можно получить в широком масштабе и не ограничивается потоками.
добавлено автор sh4kesbeer, источник
Во-первых: Большое спасибо, вы, ребята, здесь, в stackoverflow, великолепны! Надеюсь, когда-нибудь у меня будет достаточно навыков, чтобы помогать людям, как и вы. Второе: теперь, когда я знаю, что ConnectionHandler разрушен сразу после его создания, я начинаю исследовать это, без вашего любезного намека, я бы, вероятно, просто отказался от этого проекта, потому что я пытался исправить ошибку в течение недели :)
добавлено автор sh4kesbeer, источник
@ sh4kesbeer: Я переустановил ваш тестовый/экспериментальный ConnectionHandler класс, чтобы показать вам, о чем я говорил. Обратите внимание, что использование :: std :: atomic_bool было преднамеренным и абсолютно необходимым для правильной работы.
добавлено автор Omnifarious, источник
@ sh4kesbeer: Когда вы делаете что-то подобное, вам нужно подумать о том, как потоки взаимодействуют очень тщательно.
добавлено автор Omnifarious, источник
@ sh4kesbeer: Ах, это сложная ситуация. Исходный поток все еще работает, вы понимаете. И как только эта функция Acceptor будет завершена, она вернется обратно к исходной функции ConnectionHandler , которая теперь имеет недопустимый указатель this . Если вы примете решение, подобное предложению, у вас будет тупик, так как деструктор ConnectionHandler ждет ConnectionHandler :: main , чтобы заметить, что он уничтожается и заканчивается поток, поток, из которого вызван деструктор, и приостановлен.
добавлено автор Omnifarious, источник
@ sh4kesbeer: это отключить конструктор копирования по умолчанию и оператор присваивания по умолчанию. В основном я говорю, что ConnectionHandler не может быть скопирован. И я не понимаю ваш второй комментарий.
добавлено автор Omnifarious, источник
@ sh4kesbeer: если ConnectionHandler по-прежнему создает отдельный поток, вам все равно нужно указать уведомление о отключенном потоке, который он должен закрыть, а затем join с ним в < код> ConnectionHandler деструктор.
добавлено автор Omnifarious, источник
@ sh4kesbeer: Это все еще вероятно неправильное решение. Вам нужно подумать о том, какой поток «принадлежит» объекту ConnectionManager. В этом случае единственным реальным выбором является выделенный поток, так как этот поток нуждается в объекте до тех пор, пока он живет, и вы не можете его отключить. Это означает, что вам нужно иметь способ «спросить» отдельный поток, чтобы уничтожить ваш объект и закрыть его. Создавая его с помощью new видов работ, но он создает утечку памяти и в целом является беспорядочным решением.
добавлено автор Omnifarious, источник
pro.cxx
pro.cxx
3 049 участник(ов)

C/C++ chat 0. Простые вопросы, лабы и о IDE — в чат новичков @supapro 1. Не хамим, не переходим на личности, не вбрасываем утверждения без доказательств 2. No Ads, offtop, flood Объявления о вакансиях и евенты - в лс @AlexFails https://t.me/ProCxx/259155

supapro.cxx
supapro.cxx
1 925 участник(ов)

Чат для тех, кто немного знает C++, простые вопросы по реализации, синтаксису и ide – сюда, а для другого есть: /Главный чат по серьезным вопросам — @ProCxx /Чат по обсуждению всего — @fludpac

C++ Russia
C++ Russia
384 участник(ов)

Сообщество разработчиков C++ в Telegram.

cxx.Дискуссионная
cxx.Дискуссионная
298 участник(ов)

это не двач, общайтесь вежливо; разговор на почти любые темы; Не согласны с баном? В лс @AlexFails, @ivario

C++ для маленьких и тупых
C++ для маленьких и тупых
105 участник(ов)

Лоу левел (по среднему IQ участников) чатик ExtremeCode @extremecode Флудилка @extremecode_rest