c ++ - проблема с памятью в многопоточности - PullRequest
0 голосов
/ 26 марта 2019

В настоящее время я изучаю потоки в c ++ в универе, и у меня есть небольшой проект, включающий ncurses - прыгающие шары. Я хочу, чтобы шары появлялись, пока я не нажму 'x'. После того, как я нажимаю кнопку, она выходит, но также показывает что-то о нарушении защиты памяти.

Когда я использую GDB, после нажатия 'x' он говорит:

Резьба 1 "p", получен сигнал SIGSEGV, Ошибка сегментации. 0x00007ffff6e6b3c1 в _int_malloc (av = av @ entry = 0x7ffff71c2c40, bytes = bytes @ entry = 28) в malloc.c: 3612

Возможно, проблема в цикле for, но я не уверен.

Вот код, который я написал:

#include "window.h"
#include <stdlib.h>
#include <time.h>
#include <thread>
#include <unistd.h>
#include <ncurses.h>
#include <atomic>

Window *window;
std::atomic<bool> run(true);


void exit() {
    while(run) {
        char z = getch();
        if(z == 'q') run = false;
    }
}

void ballFunction(int a) {
    int nr = a;
    while (run && window->balls[nr]->counter < 5) {
        usleep(50000);
        window->balls[nr]->updateBall();
    }
    window->balls[nr]->x = -1;
    window->balls[nr]->y = -1;
} 

void updateWindow2() {
    while(run) {
        usleep(50000);
        window->updateWindow();
    }
    delete window;
}

int main() {
    srand(time(NULL));

    window = new Window();

    int i = 0;

    std::vector<std::thread> threads;

    std::thread threadWindow(updateWindow2);
    std::thread threadExit(exit);

    while(run = true) {
         window->addBall();
         threads.push_back(std::thread(ballFunction, i));
         i++;
         sleep(1);
    }

    threadWindow.join();
    threadExit.join();

    for(int j=2; j<i+2; j++) {
        threads[j].join();
    }

    return 0;
    }

#include "window.h"
#include <ncurses.h>
#include <unistd.h>

Window::Window()
{
    initWindow();
}

Window::~Window()
{
    endwin(); 
}

void Window::initWindow()
{
    initscr();
    noecho();
    curs_set(FALSE); 
    clear();
    refresh();
}

void Window::addBall()
{
    Ball *ball = new Ball(this->ballCounter++);
    this->balls.push_back(ball);
}

void Window::updateWindow()
{
    for(int i = 0; i<ballCounter; i++)
    {
        if(balls[i]->update())
        {
            clear(balls[i]->yy, 
                balls[i]->xx);
            drawBall(balls[i]->y,
                 balls[i]->x);
        }
    }
    refresh();
}

void Window::clear(int y, int x)
{
    mvprintw(y, x, " ");
}

void Window::drawBall(int y, int x)
{
    mvprintw(y, x, "o"); 
}

1 Ответ

0 голосов
/ 26 марта 2019

Проблема в том, что у вас есть один поток, добавляющий к вектору balls, в то время как другие потоки читают из него.

Когда addBall вызывает push_back, если емкость вектора недостаточно велика, будет выделена новая память, существующие объекты перемещены или скопированы в новый блок, а старая память освобождена. Когда это происходит, потоки, работающие ballFunction, могут записывать в память, которая больше не выделяется. Это приводит к неопределенному поведению, включая ошибку сегментации, с которой вы столкнулись.

Вам необходимо использовать мьютекс какой-либо формы или убедиться, что для вектора balls выделено достаточно места (используется balls.reserve(more_elements_than_youll_ever_create)).

На неродственной ноте ваш основной цикл while никогда не прекратит работу, потому что в качестве условия используется назначение, а не сравнение (используйте while (run) или while (run == true)).

...