面试题答案
一键面试NULL指针在C语言多线程编程中的挑战
- 线程安全问题:多个线程可能同时访问一个指向NULL的指针,导致未定义行为。例如,一个线程可能检查指针是否为NULL,然后在另一个线程将其设为NULL后,第一个线程尝试解引用该指针,从而引发程序崩溃。
- 竞争条件:当一个线程释放一个指针所指向的内存,并将指针设为NULL,而另一个线程正在检查该指针是否为NULL时,可能会出现竞争条件。如果第二个线程在第一个线程将指针设为NULL之前检查,就可能访问到已释放的内存。
结合pthread库应对挑战的代码设计
- 使用互斥锁
#include <pthread.h> #include <stdio.h> #include <stdlib.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int *ptr = NULL; void* thread_function(void* arg) { pthread_mutex_lock(&mutex); if (ptr == NULL) { ptr = (int*)malloc(sizeof(int)); if (ptr == NULL) { perror("malloc"); pthread_mutex_unlock(&mutex); pthread_exit(NULL); } *ptr = 42; } printf("Thread: Value of ptr: %d\n", *ptr); pthread_mutex_unlock(&mutex); pthread_exit(NULL); } int main() { pthread_t thread; if (pthread_create(&thread, NULL, thread_function, NULL) != 0) { perror("pthread_create"); return 1; } if (pthread_join(thread, NULL) != 0) { perror("pthread_join"); return 1; } pthread_mutex_destroy(&mutex); if (ptr != NULL) { free(ptr); } return 0; }
- 在这段代码中,互斥锁
mutex
用于保护对ptr
的访问。在检查ptr
是否为NULL以及分配内存的过程中,通过pthread_mutex_lock
和pthread_mutex_unlock
来确保同一时间只有一个线程能操作ptr
。
- 在这段代码中,互斥锁
- 双重检查锁定
#include <pthread.h> #include <stdio.h> #include <stdlib.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int *ptr = NULL; void* thread_function(void* arg) { if (ptr == NULL) { pthread_mutex_lock(&mutex); if (ptr == NULL) { ptr = (int*)malloc(sizeof(int)); if (ptr == NULL) { perror("malloc"); pthread_mutex_unlock(&mutex); pthread_exit(NULL); } *ptr = 42; } pthread_mutex_unlock(&mutex); } printf("Thread: Value of ptr: %d\n", *ptr); pthread_exit(NULL); } int main() { pthread_t thread; if (pthread_create(&thread, NULL, thread_function, NULL) != 0) { perror("pthread_create"); return 1; } if (pthread_join(thread, NULL) != 0) { perror("pthread_join"); return 1; } pthread_mutex_destroy(&mutex); if (ptr != NULL) { free(ptr); } return 0; }
- 双重检查锁定机制在访问共享资源前,先进行一次快速的非锁定检查,然后再锁定互斥锁进行更详细的检查。这样可以减少不必要的锁竞争,提高性能。但要注意,在一些编译器优化的情况下,可能需要使用内存屏障来确保正确性。
- 使用读写锁
#include <pthread.h> #include <stdio.h> #include <stdlib.h> pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; int *ptr = NULL; void* read_thread_function(void* arg) { pthread_rwlock_rdlock(&rwlock); if (ptr != NULL) { printf("Read Thread: Value of ptr: %d\n", *ptr); } else { printf("Read Thread: ptr is NULL\n"); } pthread_rwlock_unlock(&rwlock); pthread_exit(NULL); } void* write_thread_function(void* arg) { pthread_rwlock_wrlock(&rwlock); if (ptr == NULL) { ptr = (int*)malloc(sizeof(int)); if (ptr == NULL) { perror("malloc"); pthread_rwlock_unlock(&rwlock); pthread_exit(NULL); } *ptr = 42; } pthread_rwlock_unlock(&rwlock); pthread_exit(NULL); } int main() { pthread_t read_thread, write_thread; if (pthread_create(&read_thread, NULL, read_thread_function, NULL) != 0) { perror("pthread_create"); return 1; } if (pthread_create(&write_thread, NULL, write_thread_function, NULL) != 0) { perror("pthread_create"); return 1; } if (pthread_join(read_thread, NULL) != 0) { perror("pthread_join"); return 1; } if (pthread_join(write_thread, NULL) != 0) { perror("pthread_join"); return 1; } pthread_rwlock_destroy(&rwlock); if (ptr != NULL) { free(ptr); } return 0; }
- 读写锁
pthread_rwlock
允许在多线程环境中区分读操作和写操作。读操作可以并发执行,而写操作需要独占访问。这在读取操作频繁而写入操作较少的场景下能提高性能,同时保证对ptr
的安全访问。
- 读写锁