Сделал вот такой класс двунаправленного связанного списка http://pastebin.com/JRsWwaKS. По понятным причинам он не является thread-safe. Поэтому я любые обращения к нему защищаю с помощью pthread mutex. И всегда думал - «если есть потоконебезопасный код, то защищаешь его mutex, теряешь какую-то производительность, зато работаешь как обычно». А вот нифига.
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
List list;
void* thread_func(void* arg) {
ListItem item;
while (true) {
pthread_mutex_lock(&mutex);
list.insertHead(&item);
pthread_mutex_unlock(&mutex);
}
}
int main() {
pthread_t threads[2];
pthread_create(&threads[0], nullptr, thread_func, nullptr);
pthread_create(&threads[1], nullptr, thread_func, nullptr);
while (true) {
pthread_mutex_lock(&mutex);
ListItem* item;
do {
item = list.removeTail();
} while (item);
pthread_mutex_unlock(&mutex);
}
return 0;
}
Это небольшой тестовый код. Два потока добавляют элементы в список (добавление одного и того же элемента допустимо, ведь в функциях добавления есть проверка, что элемента ещё нет в списке и они просто вернут false). А другой извлекает все доступные элементы. Пример абстрактный, в реальном приложении над элементами списка ещё и совершают всякие полезные действия (а ещё иногда спят и т. д.). Почему mutex не интегрирован в класс списка? Потому что опять же в реальном коде списки являются частями более сложных структур, которые уже имеют свои mutex. Это чтобы избежать стилистических замечаний. Вышеприведённый код 100% блокирует все операции со списком, хотя и делает это не в ООП стиле.
И в итоге что я получаю? А получаю я срабатывание assert(retval) в функции List::removeTail. А это может означать лишь одно - в списке числится (и не просто числится, а числится хвостом списка) элемент, у которого m_list == nullptr. Что и подтверждается, если посмотреть отладчиком после abort из-за проваленного assert. Значит либо ошибка в реализации алгоритма списка (но в однопоточных тестах вроде проблем не было), либо я что-то делаю не так с блокировками.
Я понимаю, что скорее всего у меня возникло некоторое непонимание с тем как писать многопоточные приложения на C++ и считаю, что данный пример позволит наконец-то во всём разобраться (ибо он не сложный, кода мало и я могу легко сделать минимально рабочий пример). А ещё я собираюсь читать книгу про многопоточное программирование в C++, однако хотелось бы получить первые замечания быстрее, чем я её прочитаю.
P. S.: Тестовый код компилирую без каких-либо оптимизаций.
UPD: Посыпаю голову пеплом. Ошибка была в том что функции добавления таки не проверяли проверку item->m_list. Сейчас всё работает.