Pthread_join() não está funcionando como deveria para popular um array em C

Eu tenho o seguinte código:

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <locale.h>
#include <time.h>

//declaração das variáveis globais
float *x;
float *y;
float *z;
pthread_mutex_t *mutexes;

typedef struct
{
    float* vetor;
    unsigned int posInicial;
    unsigned int posFinal;
    unsigned int contMutexThread;
}Thread_preenche;

typedef struct
{
    float* x;
    float* y;
    float* z;
    unsigned int posInicial;
    unsigned int posFinal;
    unsigned int contMutexThread;
}Thread_soma;

void *preencheVetores(void *argPtr){
    Thread_preenche *thread_p = (Thread_preenche*)argPtr;
    unsigned int inicio = thread_p->posInicial;
    unsigned int final = thread_p->posFinal;

    for(;inicio <= final; inicio++){
        pthread_mutex_lock(&mutexes[thread_p->contMutexThread]);
        thread_p->vetor[inicio] = (float)rand()/(float)(RAND_MAX/1.0);
        printf("x[%u] e o valor é: %f \n", inicio, x[inicio]);
        pthread_mutex_unlock(&mutexes[thread_p->contMutexThread]);
    }
    pthread_exit(0);
}

int main() {
    setlocale(LC_ALL, "Portuguese");

    //obtendo o tamanho do vetor e o número de threads que o usuário deseja
    int tamanho_vetor;
    int n;
    while (1){
    printf("Digite o tamanho do vetor: ");
    scanf("%d", &tamanho_vetor);
    printf("Digite o número de threads: ");
    scanf("%d", &n);
    if (tamanho_vetor % n != 0){
        printf("O número de threads deve ser múltiplo do tamanho do vetor. \n");
    }else{
        break;
    }
    }

    //inicialização dos vetores
    x =(float*) malloc(sizeof(float)*tamanho_vetor);
    y =(float*) malloc(sizeof(float)*tamanho_vetor);
    z =(float*) malloc(sizeof(float)*tamanho_vetor);
    mutexes = (pthread_mutex_t*) malloc(sizeof(pthread_mutex_t)*n);
    int divisao_vetor[n+1];
    for (int i = 0; i <= n; i++){
        divisao_vetor[i] = ((tamanho_vetor/n)*(i));
    }
    pthread_t thread[n];
    for (int i = 0; i < n; ++i){
    pthread_mutex_init(&mutexes[i],NULL);
    Thread_preenche thread_preenche = {
        .vetor = &x[0],
        .posInicial = (divisao_vetor[i]),
        .posFinal = divisao_vetor[i+1]-1,
        .contMutexThread = i
    };
       pthread_create(&thread[i], NULL, preencheVetores, &(thread_preenche));
       //jeito errado, o join logo em seguida do create da thread.
       //pthread_join(thread[i], NULL);
    }
    //Assim seria o jeito certo de fazer, o join sendo depois
    //mas não está funcionando.
    for (int i = 0; i < n; i++){
        pthread_join(thread[i], NULL);
    }

    free(x);
    free(y);
    free(z);
    free(mutexes);

    #ifdef _WIN32
        system("pause");
    #else __linux__
        system("read -p 'Press Enter to continue...\n' key");
    #endif
    
   return 0;
}

void *somaVetores(void* argPtr);

Eu tenho o seguinte código:

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <locale.h>
#include <time.h>

//declaração das variáveis globais
float *x;
float *y;
float *z;
pthread_mutex_t *mutexes;

typedef struct
{
    float* vetor;
    unsigned int posInicial;
    unsigned int posFinal;
    unsigned int contMutexThread;
}Thread_preenche;

typedef struct
{
    float* x;
    float* y;
    float* z;
    unsigned int posInicial;
    unsigned int posFinal;
    unsigned int contMutexThread;
}Thread_soma;

void *preencheVetores(void *argPtr){
    Thread_preenche *thread_p = (Thread_preenche*)argPtr;
    unsigned int inicio = thread_p->posInicial;
    unsigned int final = thread_p->posFinal;

    for(;inicio <= final; inicio++){
        pthread_mutex_lock(&mutexes[thread_p->contMutexThread]);
        thread_p->vetor[inicio] = (float)rand()/(float)(RAND_MAX/1.0);
        printf("x[%u] e o valor é: %f \n", inicio, x[inicio]);
        pthread_mutex_unlock(&mutexes[thread_p->contMutexThread]);
    }
    pthread_exit(0);
}

int main() {
    setlocale(LC_ALL, "Portuguese");

    //obtendo o tamanho do vetor e o número de threads que o usuário deseja
    int tamanho_vetor;
    int n;
    while (1){
    printf("Digite o tamanho do vetor: ");
    scanf("%d", &tamanho_vetor);
    printf("Digite o número de threads: ");
    scanf("%d", &n);
    if (tamanho_vetor % n != 0){
        printf("O número de threads deve ser múltiplo do tamanho do vetor. \n");
    }else{
        break;
    }
    }

    //inicialização dos vetores
    x =(float*) malloc(sizeof(float)*tamanho_vetor);
    y =(float*) malloc(sizeof(float)*tamanho_vetor);
    z =(float*) malloc(sizeof(float)*tamanho_vetor);
    mutexes = (pthread_mutex_t*) malloc(sizeof(pthread_mutex_t)*n);
    int divisao_vetor[n+1];
    for (int i = 0; i <= n; i++){
        divisao_vetor[i] = ((tamanho_vetor/n)*(i));
    }
    pthread_t thread[n];
    for (int i = 0; i < n; ++i){
    pthread_mutex_init(&mutexes[i],NULL);
    Thread_preenche thread_preenche = {
        .vetor = &x[0],
        .posInicial = (divisao_vetor[i]),
        .posFinal = divisao_vetor[i+1]-1,
        .contMutexThread = i
    };
       pthread_create(&thread[i], NULL, preencheVetores, &(thread_preenche));
       //jeito errado, o join logo em seguida do create da thread.
       //pthread_join(thread[i], NULL);
    }
    //Assim seria o jeito certo de fazer, o join sendo depois
    //mas não está funcionando.
    for (int i = 0; i < n; i++){
        pthread_join(thread[i], NULL);
    }

    free(x);
    free(y);
    free(z);
    free(mutexes);

    #ifdef _WIN32
        system("pause");
    #else __linux__
        system("read -p 'Press Enter to continue...\n' key");
    #endif
    
   return 0;
}

void *somaVetores(void* argPtr);

Crio várias threads, cada uma para popular uma parte do vetor. Se eu fizer do jeito errado que é fazer o join logo depois do create das threads dentro de um for, desse jeito:

pthread_create(&thread[i], NULL, preencheVetores, &(thread_preenche));
pthread_join(thread[i], NULL);

Eu tenho o resultado esperado, com todos os campos populados. Mas, foi removida toda concorrência das threads, oque não é desejado.

Se eu aplico o join nas threads depois que todas elas foram criadas, desssa forma:

//abstraindo o for que cria as threads.
pthread_create(&thread[i], NULL, preencheVetores, &(thread_preenche));

for (int i = 0; i < n; i++){
pthread_join(thread[i], NULL);
}

Eu não tenho o resultado desejado, são populado apenas os últimos elementos do array, como base na divisão que fiz de acordo com o número de threads, é como se todas as threads estivessem populando o mesmo range do array, embora tivesse definido ranges diferentes para cada thread popular o array. Porque isso está acontecendo ? Onde estou errado ? Se eu removo os pthread_join(), também tenho o mesmo resultado.

Como exemplo, eu tenho o seguinte resultado (x é o array que estou populando):

Digite o tamanho do vetor: 32
Digite o número de threads: 4
x[24] e o valor é: 0.840188 
x[25] e o valor é: 0.394383 
x[26] e o valor é: 0.783099 
x[27] e o valor é: 0.798440 
x[28] e o valor é: 0.911647 
x[29] e o valor é: 0.197551 
x[30] e o valor é: 0.335223 
x[31] e o valor é: 0.768230 
x[24] e o valor é: 0.277775 
x[25] e o valor é: 0.553970 
x[26] e o valor é: 0.477397 
x[27] e o valor é: 0.628871 
x[28] e o valor é: 0.364784 
x[29] e o valor é: 0.513401 
x[30] e o valor é: 0.952230 
x[31] e o valor é: 0.916195 
x[24] e o valor é: 0.635712 
x[25] e o valor é: 0.717297 
x[26] e o valor é: 0.141603 
x[27] e o valor é: 0.606969 
x[28] e o valor é: 0.016301 
x[29] e o valor é: 0.242887 
x[30] e o valor é: 0.137232 
x[31] e o valor é: 0.804177 
x[24] e o valor é: 0.156679 
x[25] e o valor é: 0.400944 
x[26] e o valor é: 0.129790 
x[27] e o valor é: 0.108809 
x[28] e o valor é: 0.998924 
x[29] e o valor é: 0.218257 
x[30] e o valor é: 0.512932 
x[31] e o valor é: 0.839112