проблема со свободным мьютексом

#c #mutex

#c #мьютекс

Вопрос:

Всем привет, был бы признателен за любую помощь в решении небольшой проблемы, с которой я столкнулся. В принципе, моя программа работает нормально, но не проходит все требуемые тесты. По-видимому, проблема не в выполнении одного из следующих:

  • чтобы вы присоединялись ко всем потокам, которые вы создаете
  • что вы уничтожаете все мьютексы, которые вы инициализируете
  • что вы разблокируете все мьютексы, которые вы блокируете

кто-нибудь может увидеть, где я ошибся? Был бы очень признателен за любую помощь, спасибо

    #include "counter.h"

/* ============================================================================
 * File-global variables
 * ========================================================================== */
static int ncounters = 0;
static struct counter *counters = NULL;

static int nthreads = 0;
static int *ninstructions = NULL;
static struct instruction **instructions = NULL;


/* ============================================================================
 * Operations
 * ========================================================================== */
static void
decrement(long long *n) {
    *n = *n-1;
}

static void
increment(long long *n) {
    *n = *n 1;
}

static void
mult2(long long *n) {
    long long s = 2;
    long long t = *n;
    long long q = t*s;
    *n = q;
}


/* ============================================================================
 * Helper functions
 * ========================================================================== */

int
quit(void) {
    int i;
    for (i=0; i<nthreads;   i) {
        free (instructions[i]);
    }
    free (instructions);
    for (i=0; i<ncounters;   i) {
        pthread_mutex_t *my = amp;(counters[i].mylock);
        pthread_mutex_destroy(my);
    }
    free (counters);
    free (ninstructions);
    return 0;
}

/* ============================================================================
 * Thread function
 * ========================================================================== */
static void *
worker_thread(void *arg) {
    int t = *((int*)arg);
    int l;
    for (l = 0; l<ninstructions[t];   l) {
        int y;
        struct instruction* curr = amp;instructions[t][l];
        pthread_mutex_lock(amp;curr->counter->mylock);
        for (y=0; y<curr->repetitions;   y) {
            long long *g = amp;curr->counter->counter;
            (curr->work_fn)(g);
        }
        pthread_mutex_unlock(amp;curr->counter->mylock);
    }

    return NULL;
}


/* ============================================================================
 * Main function
 * ========================================================================== */
int
main(void) {
    if (scanf("%d", amp;ncounters) != 1 || ncounters < 1) {
        printf("errorn");
        return quit();
    }
    counters = (struct counter*)malloc(ncounters*sizeof(struct counter));


    if (scanf(" %d", amp;nthreads) != 1 || nthreads < 1) {
        printf("errorn");
        return quit();
    }
    ninstructions = (int *)malloc(nthreads*sizeof(int));
    instructions = (struct instruction**)malloc(nthreads*sizeof(struct instruction*));
    int i;
    for (i=0; i<nthreads;   i) {

        if (scanf(" %d", amp;ninstructions[i]) != 1) {
            printf("errorn");
            return quit();
        }
        instructions[i] = (struct instruction*)malloc(ninstructions[i]*sizeof(struct instruction));
        int k;
        for (k=0; k<ninstructions[i];   k) {
            int c, r;
            char f;
            if (scanf(" %d %c %d", amp;c, amp;f, amp;r) != 3 || c>ncounters-1) {
                printf("errorn");
                return quit();
            }
            struct instruction* curr = amp;instructions[i][k];
            struct counter* currcp = amp;counters[c];
            pthread_mutex_init (amp;currcp->mylock, NULL);
            curr->counter = currcp;
            curr->repetitions = r;
            switch(f) {
                case 'I': 
                    curr->work_fn = increment;
                    break;
                case 'D':
                    curr->work_fn = decrement;
                    break;
                case '2':
                    curr->work_fn = mult2;
                    break;
                default:
                    printf("errorn");
                    return quit();
            }
        }
    }
    int w;
    pthread_t threadIDs[nthreads];
    int args[nthreads];
    for (w=0; w<nthreads;   w) {
        args[w] = w;
        pthread_create(amp;threadIDs[w], NULL, worker_thread, (void *) amp;args[w]);
    }
    int u;
    for (u=0; u<nthreads;   u) {
        pthread_join(threadIDs[u], NULL);   
    }
    int d;
    for (d=0; d<ncounters;   d) {
        printf("%lldn", counters[d].counter);
    }
    return quit();
}
  

и структура данных

 #ifndef __COUNTER_H__
#define __COUNTER_H__

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


/* counter data structure */ 
struct counter {
    pthread_mutex_t mylock;
   long long counter;            /* to store counter */
};

/* counter value */
struct instruction {
   struct counter *counter;      /* pointer to counter */
   int repetitions;              /* number of repetitions  */
   void (*work_fn)(long long *); /* function pointer to work function */
};

#endif
  

да, извините, я думал, что это может быть возможно без формата:

 <number of counters>
<number of threads>
<instruction-sequence>
<instruction-sequence>
....
<number of instructions>
<instruction>
<instruction>
....
  

итак, для каждой последовательности команд у вас есть список инструкций, которые выполняются каждым потоком на одном или нескольких счетчиках.
т.е. входные:

 2
2
1
0 I 10
2
1 D 10
1 2 2
  

приведет к результату:

 10
-40
  

(существует только три типа команд: инкремент (I), декремент (D), многократно на 2 (2)
где инструкция имеет формат:

 <counter> <function> <repitition>
  

имеет ли это смысл?

Комментарии:

1. Не хотите поделиться полезной информацией? Довольно сложно составлять числа случайным образом из вашего источника

2. проблема в том, что входные данные для тестирования не разглашаются, поэтому я понятия не имею, какой тест я проваливаю

Ответ №1:

Что ж, похоже, что вы объединяете все потоки, которые вы создаете, и разблокируете все mutices, которые вы блокируете — эта часть довольно проста.

Однако, рассматривая второе условие, кажется, что нет очевидной корреляции между созданием и уничтожением мьютексов.

Вы вызываете pthread_mutex_init один раз для каждой инструкции, тогда как вы вызываете pthread_mutex_destroy один раз для каждого счетчика. Я не вижу никакой гарантии, что количество инструкций равно количеству счетчиков.

Я бы предположил, что вам нужен один мьютекс на счетчик. Следовательно, не имеет смысла вводить один мьютекс для каждой инструкции. Вероятно, вам нужна процедура инициализации, которая вводит мьютекс для каждого счетчика, чтобы отразить вашу quit процедуру, которая уничтожает мьютекс для каждого счетчика.

Комментарии:

1. да, я думаю, вы поняли, поскольку у вас может быть (и, вероятно, будет) больше инструкций, чем счетчиков. Теперь я перебрал каждый счетчик в массиве и инициализировал мьютексы. Большое спасибо за вашу помощь 🙂

2. Спасибо. Если вы обнаружите, что это решает вашу проблему, пожалуйста, отметьте это белой галочкой рядом с моим ответом.

Ответ №2:

с моим собственным вводом

 10
3
2
1 I 3
2 D 4
2
3 2 5
4 2 6
2
5 I 7
6 D 8
  

вывод был

 $ ./t.exe < i
0
3
-4
0
0
7
-8
0
0
0
  

Используя ваш ввод, я получил ожидаемый результат. В чем вопрос?

Обновить

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

В этом случае создайте входные данные для модульного тестирования и

  • обязательно коснитесь граничных случаев (граничных условий)
  • запустите их под valgrind, если это Linux; Таким образом, вы будете уведомлены, если что-то просочится.

Комментарии:

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

2. Это произойдет косвенно, поскольку мьютекст также займет некоторое количество памяти. Кроме того, вы могли бы использовать —tool=helgrind, чтобы посмотреть, какие проверки работоспособности он бормочет