多线程编程基础
1. 线程与进程的区别
-
进程:操作系统资源分配的基本单位,拥有独立的内存空间。
-
线程:进程内的执行单元,共享同一进程的内存空间,切换开销更小。 一个进程可包含多个线程。
2. 线程的创建与生命周期
-
创建:通过
pthread_create()
函数定义线程。 -
终止:自然结束(函数返回)或显式调用
pthread_exit()
。 -
回收资源:父线程通过
pthread_join()
等待子线程结束。
3. 线程同步机制
-
互斥锁(Mutex):保护共享资源,避免数据竞争。
-
条件变量(Condition Variable):线程间通信,基于特定条件唤醒等待线程。
-
信号量(Semaphore):控制对共享资源的并发访问数量。
注意:在编译多线程程序时需链接pthread库:
gcc program.c -o program -lpthread
多线程编程训练题
以下是从基础到进阶的多线程编程题目,涵盖线程操作、同步机制与并发控制。
1. 简单线程创建与参数传递
题目描述 创建两个线程,分别打印传入的整数值(如10和20)。主线程等待子线程结束后输出All threads completed
。
示例输出
Thread 1 value: 10
Thread 2 value: 20
All threads completed
参考代码
#include <stdio.h>
#include <pthread.h>
void* print_value(void* arg) {
int value = *(int*)arg;
printf("Thread value: %d\n", value);
pthread_exit(NULL);
}
int main() {
pthread_t t1, t2;
int val1 = 10, val2 = 20;
pthread_create(&t1, NULL, print_value, &val1);
pthread_create(&t2, NULL, print_value, &val2);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
printf("All threads completed\n");
return 0;
}
2. 多线程计算数组和
题目描述 将一个数组分为两部分,由两个线程分别计算各部分的累加和,主线程汇总后输出总和。
示例输入 数组:[1, 2, 3, 4, 5, 6] 示例输出
Total sum: 21
参考代码
#include <stdio.h>
#include <pthread.h>
#define ARRAY_SIZE 6
int sum = 0;
pthread_mutex_t mutex;
typedef struct {
int* array;
int start;
int end;
} ThreadData;
void* partial_sum(void* arg) {
ThreadData* data = (ThreadData*)arg;
int local_sum = 0;
for (int i = data->start; i < data->end; i++) {
local_sum += data->array[i];
}
pthread_mutex_lock(&mutex);
sum += local_sum;
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
int main() {
int arr[ARRAY_SIZE] = {1, 2, 3, 4, 5, 6};
pthread_t t1, t2;
ThreadData d1 = {arr, 0, ARRAY_SIZE/2};
ThreadData d2 = {arr, ARRAY_SIZE/2, ARRAY_SIZE};
pthread_mutex_init(&mutex, NULL);
pthread_create(&t1, NULL, partial_sum, &d1);
pthread_create(&t2, NULL, partial_sum, &d2);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_mutex_destroy(&mutex);
printf("Total sum: %d\n", sum);
return 0;
}
3. 生产者-消费者问题(条件变量)
题目描述 实现一个生产者-消费者模型:
-
生产者线程生成1~100的数字存入缓冲区。
-
消费者线程从缓冲区取出数字并打印。
-
缓冲区大小为5,满时生产者等待,空时消费者等待。
示例输出片段
Produced: 1
Consumed: 1
Produced: 2
Consumed: 2
...
参考代码框架
#include <stdio.h>
#include <pthread.h>
#define BUFFER_SIZE 5
int buffer[BUFFER_SIZE], count = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_producer = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_consumer = PTHREAD_COND_INITIALIZER;
void* producer(void* arg) {
for (int i = 1; i <= 100; i++) {
pthread_mutex_lock(&mutex);
while (count == BUFFER_SIZE)
pthread_cond_wait(&cond_producer, &mutex);
buffer[count++] = i;
printf("Produced: %d\n", i);
pthread_cond_signal(&cond_consumer);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
void* consumer(void* arg) {
for (int i = 0; i < 100; i++) {
pthread_mutex_lock(&mutex);
while (count == 0)
pthread_cond_wait(&cond_consumer, &mutex);
int num = buffer[--count];
printf("Consumed: %d\n", num);
pthread_cond_signal(&cond_producer);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main() {
pthread_t t1, t2;
pthread_create(&t1, NULL, producer, NULL);
pthread_create(&t2, NULL, consumer, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
return 0;
}
4. 互斥锁实现线程安全计数器
题目描述 创建4个线程,每个线程对全局计数器累加2500次,确保最终结果为10000。使用互斥锁防止数据竞争。
示例输出
Final counter value: 10000
参考代码
#include <stdio.h>
#include <pthread.h>
#define THREADS 4
#define INCREMENTS 2500
int counter = 0;
pthread_mutex_t mutex;
void* increment(void* arg) {
for (int i = 0; i < INCREMENTS; i++) {
pthread_mutex_lock(&mutex);
counter++;
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main() {
pthread_t threads[THREADS];
pthread_mutex_init(&mutex, NULL);
for (int i = 0; i < THREADS; i++)
pthread_create(&threads[i], NULL, increment, NULL);
for (int i = 0; i < THREADS; i++)
pthread_join(threads[i], NULL);
pthread_mutex_destroy(&mutex);
printf("Final counter value: %d\n", counter);
return 0;
}
5. 信号量实现读者-写者问题
题目描述 用信号量实现读者优先的读者-写者模型:
-
允许多个读者同时读取共享资源。
-
写者必须独占访问资源。
参考框架
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
sem_t rw_mutex, mutex;
int readers = 0;
void* reader(void* arg) {
sem_wait(&mutex);
readers++;
if (readers == 1) sem_wait(&rw_mutex); // 第一个读者锁住写者
sem_post(&mutex);
// 读取操作...
sem_wait(&mutex);
readers--;
if (readers == 0) sem_post(&rw_mutex); // 最后一个读者释放写者
sem_post(&mutex);
return NULL;
}
void* writer(void* arg) {
sem_wait(&rw_mutex);
// 写入操作...
sem_post(&rw_mutex);
return NULL;
}
int main() {
sem_init(&rw_mutex, 0, 1);
sem_init(&mutex, 0, 1);
// 创建读者和写者线程...
return 0;
}
进阶挑战
6. 动态线程池实现
题目描述 实现一个线程池:主线程提交任务到任务队列,空闲线程从队列中取出任务执行。支持动态调整线程数量。
关键步骤
-
使用队列存储任务(函数指针+参数)。
-
动态创建/销毁线程。
-
通过互斥锁和条件变量管理任务队列。
训练总结
通过以上题目,可掌握C语言中:
-
基本的线程创建与管理。
-
互斥锁、条件变量、信号量的使用场景。
-
经典并发问题(如生产者-消费者、读者-写者)的解决方案。
-
多线程程序调试技巧(死锁分析、竞态条件检查)。 建议结合调试工具(如Valgrind)确保线程安全。