Семафор не работает правильно с несколькими потоками

Я пытаюсь написать пример с несколькими потоками с помощью C. Это число получает от пользователя и суммирует числа в двух потоках.

Нити находятся непосредственно друг напротив друга, спереди назад и сзади наперед.

Я использую семафор для управления глобальными переменными, но вывод показывает, что семафор работает неправильно.

Что не так?

Вывод кода:

Array size? :10
Array of contents? :4 2 8 1 5 3 7 9 6 10
Thread_1 > read 4, sum is = 4 
Thread_1 > read 2, sum is = 6 
Thread_1 > read 8, sum is = 14 
Thread_1 > read 1, sum is = 15 
Thread_1 > read 5, sum is = 20 
Thread_1 > read 3, sum is = 23 
Thread_1 > read 7, sum is = 30 
Thread_1 > read 9, sum is = 39 
Thread_1 > read 6, sum is = 45 
Thread_1 > read 10, sum is = 55 
Thread_1 > End. 
Thread_2 > End. 

Код :

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <semaphore.h>

int *array; //Global integer array
int sum;    //Global sum variable
int counter = 0;

sem_t mutex;

void *front_to_back( void *val )
{
  int SIZE;
  SIZE = *((int *) val);

  while (1)
  {
    sem_wait(&mutex);

    if( counter != SIZE )
    {
      sum = sum + array[counter];

      printf("Thread_1 > read %d, sum is = %d \n", array[counter], sum);

      counter++;

    }else
    {
      printf("Thread_1 > End. \n");

      sem_post(&mutex);

      break;
    }

    sem_post(&mutex);
  }
}


void *back_to_front( void *val )
{
  int SIZE;
  SIZE = *((int *) val);

  int i=1;

  while (1)
  {
    sem_wait(&mutex);

    if( counter != SIZE )
    {
      sum = sum + array[SIZE - i];

      printf("Thread_2 > read %d, sum is = %d \n", array[SIZE - i], sum);

      counter++;
      i++;

    }else
    {
      printf("Thread_2 > End. \n");

      sem_post(&mutex);

      break;
    }

    sem_post(&mutex);
  }
}

main(){

  int SIZE;

  printf("Array size? :");
  scanf("%d", &SIZE);

  array = malloc(SIZE*sizeof(int));

  int i=0;

  printf("Array of contents? :");
  for (i ; i<SIZE ; i++)
  {
    scanf("%d", &array[i]);
  }

  pthread_t t[2];

  sem_init(&mutex, 0, 1);

  pthread_create(&t[1], NULL, front_to_back, (void *) &SIZE );
  pthread_create(&t[2], NULL, back_to_front, (void *) &SIZE );

  pthread_join(t[1],NULL);
  pthread_join(t[2],NULL);

  free(array);
  sem_destroy(&mutex);

}

person Erol Guzoğlu    schedule 17.05.2015    source источник


Ответы (1)


Вы должны использовать t[0] и t[1] в main(): массив размером N имеет действительные индексы в 0..N-1, поэтому сначала исправьте это. Вот правильный код:

pthread_create(&t[0], NULL, front_to_back, (void *) &SIZE );
pthread_create(&t[1], NULL, back_to_front, (void *) &SIZE );

pthread_join(t[0],NULL);
pthread_join(t[1],NULL);

В идеале вы также должны проверить, успешно ли выполняются malloc() и pthread_create(). Эти функции могут возвращаться с ошибкой, и вы должны с этим справиться. Ваш код игнорирует ошибки (с доступом за пределами границ вам действительно повезло, что он вообще работал).

Кроме того, почему вы используете семафор, как если бы это был мьютекс? Здесь семафор на самом деле не нужен — когда вам не нужна мощность и гибкость семафора, просто используйте мьютекс. Одно важное отличие заключается в том, что семафоры позволяют вам ожидать в одном потоке и отправлять сообщения в другом, в то время как мьютекс должен быть разблокирован потоком, который его захватил.

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

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

int *array; //Global integer array
int sum;    //Global sum variable
int counter = 0;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void *front_to_back( void *val )
{
    int SIZE;
    SIZE = *((int *) val);

    while (1)
    {
        pthread_mutex_lock(&mutex);

        if( counter != SIZE )
        {
            sum = sum + array[counter];

            printf("Thread_1 > read %d, sum is = %d \n", array[counter], sum);

            counter++;

        }else
        {
            printf("Thread_1 > End. \n");

            pthread_mutex_unlock(&mutex);

            break;
        }

        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}


void *back_to_front( void *val )
{
    int SIZE;
    SIZE = *((int *) val);

    int i=1;

    while (1)
    {
        pthread_mutex_lock(&mutex);

        if( counter != SIZE )
        {
            sum = sum + array[SIZE - i];

            printf("Thread_2 > read %d, sum is = %d \n", array[SIZE - i], sum);

            counter++;
            i++;

        }else
        {
            printf("Thread_2 > End. \n");

            pthread_mutex_unlock(&mutex);

            break;
        }

        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main(void) {

    int SIZE;

    printf("Array size? :");
    scanf("%d", &SIZE);

    array = malloc(SIZE*sizeof(int));

    int i=0;

    printf("Array of contents? :");
    for (; i<SIZE ; i++)
    {
        scanf("%d", &array[i]);
    }

    pthread_t t[2];

    pthread_create(&t[0], NULL, front_to_back, (void *) &SIZE );
    pthread_create(&t[1], NULL, back_to_front, (void *) &SIZE );

    pthread_join(t[0],NULL);
    pthread_join(t[1],NULL);

    free(array);

    return 0;

}

Других проблем с кодом на первый взгляд не вижу.

Теперь похоже, что ваша проблема в том, что вы не видите никакого параллелизма. Вы должны попробовать с большим массивом. Один поток, вероятно, сможет обработать массив из 10 элементов до переключения контекста.

ОБНОВЛЕНИЕ: я только что попробовал код с массивом из 3000 элементов. Вы можете ясно видеть, как они выполняются параллельно.

person Filipe Gonçalves    schedule 17.05.2015
comment
Вы правы, это ускользнуло от меня. Но изменений нет. Я снова получил тот же результат. Только один поток работает, другой поток просто ждет все время выполнения. Также спасибо за совет по обработке ошибок. Я буду работать над этим. - person Erol Guzoğlu; 17.05.2015
comment
@ErolGuzoğlu См. обновленный ответ. 10 элементов не очень много. Вам нужно протестировать его с большими массивами, чтобы каждый поток не заканчивал работу до переключения контекста. - person Filipe Gonçalves; 17.05.2015
comment
Спасибо за поддержку. Я попробовал свой код с массивом из 3000 элементов, как вы говорите. Я вижу, что мой код работает правильно. - person Erol Guzoğlu; 17.05.2015