2

Разбираюсь с синхронизацией потоков, в роли функции для второго потока является получение элементов очереди (queue).

Столкнулся с возникновением ошибки сегментирования, при выводе элементов очереди в потоке. Если выводить элементов ~1000, ошибки сегментирования не возникает, при выводе например 2500 элементов возникает ошибка и сигнал SIGSEGV убивает процесс. я подразумеваю что ошибка в функции get_queue и программа лезет в чужую область памяти.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

#define COUNT 2500

typedef struct queue_s {
    const char* data;
    struct queue_s* next;
} queue_t;

queue_t* init_queue(const char* str)
{
    queue_t* queue = (queue_t*) malloc(sizeof(queue_t));
    char* data = (char*) malloc(strlen(str));

    queue->next = NULL;
    strcpy(data, str);
    queue->data = data;

    return queue;
}

void add_queue(queue_t* queue, const char* str)
{
    while (queue->next != NULL)
        queue = queue->next;

    queue->next = init_queue(str);
}

queue_t* get_queue(queue_t* queue)
{
    const char* data = queue->data;

    printf("data = %s\n", data);

    return queue->next;
}

void thread_cycle(queue_t* queue)
{
    int i;

    for (i = 0; i < COUNT; i++)
    {
        queue = get_queue(queue);
    }
}

void *thread_func (void* arg)
{
    printf("Thread worked\n");

    queue_t* queue = (queue_t*) arg;

    thread_cycle(queue);

    return NULL;
}

void main_cycle(queue_t* queue)
{
    int i;
    char str[10];

    for (i = 0; i < COUNT; i++)
    {
        sprintf(str, "Q%d", i+1);
        add_queue(queue, str);
    }
}

int main (int argc, char *argv[])
{
    printf("Main worked\n");

    pthread_t thread;
    queue_t* queue = init_queue("QInit");

    if (pthread_create(&thread, NULL, thread_func, queue) != 0)
    {
        perror("pthread_create");
        return 1;
    }

    main_cycle(queue);

    sleep(1);

    return 0;
}

не подскажете, в чем проблема?

andreycha
  • 25,167
  • 4
  • 46
  • 82
  • Как насчет добавления проверки, что malloc возвращает не NULL? – gbg May 01 '16 at 22:22

2 Answers2

1

Сделал некоторые изменения кода для разъяснения дела.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

#define COUNT 2500

typedef struct queue_s {
    const char* data;
    struct queue_s* next;
} queue_t;

// >> флаг остановки всех потоков
int muststop = 0;

queue_t* init_queue(const char* str)
{
    queue_t* queue = (queue_t*) malloc(sizeof(queue_t));
    char* data = (char*) malloc(strlen(str));

    queue->next = NULL;
    strcpy(data, str);
    queue->data = data;

    return queue;
}

void add_queue(queue_t* queue, const char* str)
{
    while (queue->next != NULL)
        queue = queue->next;

    queue->next = init_queue(str);
}

queue_t* get_queue(queue_t* queue)
{
    const char* data = queue->data;

    printf("data = %s\n", data);

    return queue->next;
}

void thread_cycle(queue_t* queue)
{
    int i;

    for (i = 0; i < COUNT; i++)
    {
        // >> проверка перед обращением к памяти
        if (queue == NULL)
        {
            // >> вывод индекса и указание к остановке другого потока
            printf("Get queue error at i = %d.\n", i);
            muststop = 1;
            break;
        }
        queue = get_queue(queue);
    }
}

void *thread_func (void* arg)
{
    printf("Thread worked\n");

    queue_t* queue = (queue_t*) arg;

    thread_cycle(queue);

    return NULL;
}

void main_cycle(queue_t* queue)
{
    int i;
    char str[10];

    for (i = 0; i < COUNT; i++)
    {
        sprintf(str, "Q%d", i+1);
        add_queue(queue, str);
        // >> проверка на необходиость выйти
        if (muststop) {
            printf("Main cycle at i = %d.\n", i);
            break;
        }
    }
}

int main (int argc, char *argv[])
{
    printf("Main worked\n");

    pthread_t thread;
    queue_t* queue = init_queue("QInit");

    if (pthread_create(&thread, NULL, thread_func, queue) != 0)
    {
        perror("pthread_create");
        return 1;
    }

    main_cycle(queue);
    sleep(1);        
    return 0;
}

И что же получаем?

Get queue error at i = 951. Main cycle at i = 950.

Потоки идут параллельно и вся корректность выполнения зависит от того, что выполняется первее. От случая, то есть. Как это исправить? Поставить какой-нибудь sleep(1); в thread_func() перед вызовом thread_cycle(queue); Или в первом потоке ставить флаг при достаточном количестве наполнения, которого будет ждать второй поток.

P.S.: пожалуйста, давайте пояснения к своему коду, чтобы не обязательно было его запускать для понимания хода выполнения.

AivanF.
  • 9,858
1

Ваш код неверен. Любая работа с данными из нескольких потоков требует синхронизацию, например, при помощи mutex'а (нет, и не думайте про lock-free-алгоритмы, пока не освоите синхронизацию в совершенстве).

В вашем случае каждое добавление данных в очередь требует блокировки. Кроме того, вы должны отправить сигнал другому потоку, что доступны новые данные, чтобы он не ожидал их в холостом цикле.

Вот пример правильной имплементации: https://ru.stackoverflow.com/a/428867

VladD
  • 206,799