使用pthread_create出现Segmentation Fault,求问题解析与排查
Hey there! Let's walk through your problem step by step—first explaining that Valgrind error, then digging into the bugs in your code, and finally fixing it to meet your goal.
Quick Recap of Your Goal
You're learning pthreads and mutexes, aiming to write a program that:
- Spawns 4 threads
- Each thread calculates the square of
x=5and stores it in a 10-element array - Ends up with an array where all elements are 25
But you're hitting a segmentation fault, Valgrind is yelling about an "Invalid write of size 4", and even after a tweak, the program runs infinitely. Let's fix this.
What Does Valgrind's "Invalid write of size 4" Mean?
This error is Valgrind's way of saying: Your program tried to write 4 bytes (the size of a standard int in most systems) to a memory address it doesn't have permission to access. Common causes here are:
- Accessing memory beyond the bounds of an array or allocated block
- Dereferencing a null pointer or a pointer that points to garbage memory
- Writing to memory that's already been freed
Let's Debug Your Code
First, here's your code formatted for readability:
#include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <error.h> #define NTHREADS 4 static int k = 0; struct thread_info{ pthread_t thread_id; pthread_mutex_t lock; int x; int a[10]; }; void* fkt(void* arg){ struct thread_info* tinfo = (struct thread_info*) arg; if(pthread_mutex_lock(&tinfo->lock)) perror("mutex_lock"); printf("THREAD %d with argument %d\n", (int) tinfo->thread_id, tinfo->x); tinfo->a[k] = tinfo->x * tinfo->x; k++; if(pthread_mutex_unlock(&tinfo->lock)) perror("mutex_unlock"); pthread_exit(NULL); } int main(){ struct thread_info* tinfo = (struct thread_info*) malloc(sizeof(struct thread_info)); tinfo->x = 5; if(pthread_mutex_init(&tinfo->lock, NULL)) perror("mutex_init"); for(int i = 0; i < NTHREADS; i++){ if(pthread_create(&tinfo[i].thread_id, NULL, &fkt, &tinfo)) perror("pthread_create"); } for(int i = 0; i < NTHREADS; i++){ if(pthread_join(tinfo[i].thread_id, NULL)) perror("pthread_join"); printf("THREAD JOINED: %d\n", (int) tinfo->thread_id); } for(int i = 0; i < 10; i++){ printf("[%d]\t", tinfo->a[i]); } printf("\n"); if(pthread_mutex_destroy(&tinfo->lock)) perror("mutex_destroy"); return 0; }
1. The Big One: Memory Allocation & Out-of-Bounds Access
You only allocated enough memory for one thread_info struct:
struct thread_info* tinfo = malloc(sizeof(struct thread_info));
But then you treat tinfo like an array of 4 elements in your loops (tinfo[i] where i goes from 0 to 3). This is writing way beyond the memory you allocated—this is exactly what's causing the segmentation fault and Valgrind's "invalid write" error.
2. Wrong Thread Argument Type
When creating threads, you pass &tinfo—which is a pointer to a pointer (struct thread_info**)—but your thread function casts it to a struct thread_info*. This mismatch means your thread is trying to dereference garbage memory, making the problem even worse.
3. Global Variable k & Thread Safety (Secondary Issue)
While you have a mutex, k is a global variable. Even if you fix the memory issue, this works for 4 threads, but if you wanted to fill all 10 array slots, you'd need more threads. Plus, using global variables for shared state is bad practice—it makes the code harder to debug and maintain.
4. Infinite Loop?
If you modified the code and still get an infinite run, it's likely because some threads weren't created properly (due to memory issues) and pthread_join is waiting for a thread that never started.
Fixed Code Example
Here's a revised version that fixes all these issues and meets your goal:
#include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <errno.h> // Use errno.h instead of error.h for standard error handling #define NTHREADS 4 #define ARRAY_SIZE 10 struct thread_info{ pthread_t thread_id; pthread_mutex_t* shared_lock; // Share one mutex across all threads int target_x; int* shared_array; // Shared array to fill int* next_index; // Shared index tracker }; void* square_writer(void* arg){ struct thread_info* tinfo = (struct thread_info*) arg; int square = tinfo->target_x * tinfo->target_x; // Lock before accessing shared resources if(pthread_mutex_lock(tinfo->shared_lock)) { perror("Failed to lock mutex"); pthread_exit(NULL); } // Fill as many slots as needed (we'll loop until array is full) while(*tinfo->next_index < ARRAY_SIZE) { int idx = *tinfo->next_index; tinfo->shared_array[idx] = square; printf("Thread %lu filled index %d with %d\n", (unsigned long)tinfo->thread_id, idx, square); (*tinfo->next_index)++; } if(pthread_mutex_unlock(tinfo->shared_lock)) { perror("Failed to unlock mutex"); pthread_exit(NULL); } pthread_exit(NULL); } int main(){ // Allocate array to fill int* result_array = calloc(ARRAY_SIZE, sizeof(int)); if(!result_array) { perror("Failed to allocate result array"); exit(EXIT_FAILURE); } // Allocate thread info for 4 threads struct thread_info* tinfo = malloc(NTHREADS * sizeof(struct thread_info)); if(!tinfo) { perror("Failed to allocate thread info"); free(result_array); exit(EXIT_FAILURE); } // Initialize shared mutex and index tracker pthread_mutex_t shared_lock; if(pthread_mutex_init(&shared_lock, NULL)) { perror("Failed to initialize mutex"); free(tinfo); free(result_array); exit(EXIT_FAILURE); } int next_index = 0; int target_x = 5; // Create threads for(int i = 0; i < NTHREADS; i++){ tinfo[i].target_x = target_x; tinfo[i].shared_lock = &shared_lock; tinfo[i].shared_array = result_array; tinfo[i].next_index = &next_index; if(pthread_create(&tinfo[i].thread_id, NULL, square_writer, &tinfo[i])) { perror("Failed to create thread"); // Clean up existing threads on failure for(int j = 0; j < i; j++) { pthread_join(tinfo[j].thread_id, NULL); } pthread_mutex_destroy(&shared_lock); free(tinfo); free(result_array); exit(EXIT_FAILURE); } } // Wait for all threads to finish for(int i = 0; i < NTHREADS; i++){ if(pthread_join(tinfo[i].thread_id, NULL)) { perror("Failed to join thread"); } printf("Joined thread %lu\n", (unsigned long)tinfo[i].thread_id); } // Print the final array printf("\nFinal array (all elements should be 25):\n"); for(int i = 0; i < ARRAY_SIZE; i++){ printf("[%d]\t", result_array[i]); } printf("\n"); // Cleanup resources pthread_mutex_destroy(&shared_lock); free(tinfo); free(result_array); return 0; }
Key Fixes & Improvements
- Proper Memory Allocation: We allocate enough space for 4
thread_infostructs and a separate result array. - Shared Resources: We use a single mutex to protect access to the shared array and index tracker, avoiding race conditions.
- Correct Thread Arguments: We pass a pointer to each thread's specific
thread_infostruct, fixing the type mismatch. - Fill All Array Slots: Threads loop until the entire array is filled, so even with 4 threads, you'll get all 10 elements set to 25.
- Error Handling: We add cleanup code for failure cases to avoid memory leaks.
- Standard Thread ID Printing: We use
unsigned longto print thread IDs (POSIX-compliant, avoids casting issues).
内容的提问来源于stack exchange,提问作者vl_stackoverflow

