1. 线程控制概述
在Linux系统中,线程是执行程序的最小单位,是进程中的一个实体。线程可以独立执行程序的一部分,并共享进程的资源。线程控制主要涉及线程的创建、运行和终止。本文将详细介绍Linux系统下的线程控制。
1.1 线程的创建
在Linux系统中,可以使用pthread库来创建和管理线程。下面是一个简单的线程创建的示例:
#include <stdio.h>
#include <pthread.h>
void *thread_func(void *arg) {
printf("This is a thread\n");
return NULL;
}
int main() {
pthread_t tid;
pthread_create(&tid, NULL, thread_func, NULL);
pthread_join(tid, NULL);
return 0;
}
在上面的示例中,使用pthread_create函数创建了一个新的线程,指定了线程函数thread_func,并将线程的标识符存储在tid变量中。然后使用pthread_join函数等待线程的结束。通过调用pthread_join函数,主线程会阻塞直到被等待的线程结束。
1.2 线程的运行
线程的运行由系统调度器控制。在多线程程序中,多个线程可以并行执行,每个线程都有自己的执行上下文和栈空间。线程之间可以共享进程的数据和资源,这使得线程之间的通信更加方便。
下面是一个使用多线程进行并行计算的示例:
#include <stdio.h>
#include <pthread.h>
#define NUM_THREADS 4
void *thread_func(void *arg) {
int tid = *(int *)arg;
printf("This is thread %d\n", tid);
return NULL;
}
int main() {
pthread_t threads[NUM_THREADS];
int thread_args[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; i++) {
thread_args[i] = i;
pthread_create(&threads[i], NULL, thread_func, &thread_args[i]);
}
for (int i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
}
return 0;
}
在上面的示例中,创建了4个线程,并将每个线程的标识符存储在threads数组中。通过在线程函数中打印线程的标识符,可以看到线程是并行执行的。
1.3 线程的终止
线程的终止可以通过多种方式实现。一种常用的方式是在线程函数中使用return语句。当线程函数返回时,线程会自动终止。
下面是一个使用pthread_exit函数终止线程的示例:
#include <stdio.h>
#include <pthread.h>
void *thread_func(void *arg) {
printf("This is a thread\n");
pthread_exit(NULL);
}
int main() {
pthread_t tid;
pthread_create(&tid, NULL, thread_func, NULL);
pthread_join(tid, NULL);
return 0;
}
在上面的示例中,使用pthread_exit函数在线程函数中显式地终止了线程。
2. 线程同步
在多线程程序中,线程之间的访问共享资源可能会导致数据竞争和不确定性行为。为了保证线程之间的协同工作,需要使用线程同步机制。下面介绍两种常用的线程同步机制:互斥量和条件变量。
2.1 互斥量
互斥量是一种常用的保护共享资源的机制,它可以确保在同一时间只有一个线程能够访问共享资源。Linux系统提供了pthread_mutex_t类型来表示互斥量,并提供了相关的函数来操作互斥量。
下面是一个使用互斥量实现线程同步的示例:
#include <stdio.h>
#include <pthread.h>
#define NUM_THREADS 2
int counter = 0;
pthread_mutex_t mutex;
void *thread_func(void *arg) {
pthread_mutex_lock(&mutex);
for (int i = 0; i < 1000000; i++) {
counter++;
}
pthread_mutex_unlock(&mutex);
return NULL;
}
int main() {
pthread_t threads[NUM_THREADS];
pthread_mutex_init(&mutex, NULL);
for (int i = 0; i < NUM_THREADS; i++) {
pthread_create(&threads[i], NULL, thread_func, NULL);
}
for (int i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
}
pthread_mutex_destroy(&mutex);
printf("Counter value: %d\n", counter);
return 0;
}
在上面的示例中,使用互斥量保护了共享资源counter的访问。通过调用pthread_mutex_lock函数获取互斥量锁,然后对共享资源进行操作,最后调用pthread_mutex_unlock函数释放互斥量锁。这样可以确保只有一个线程能够访问共享资源,避免了数据竞争。
2.2 条件变量
条件变量是一种常用的线程同步机制,它允许线程在某个条件成立时等待,只有条件成立时,其他线程才能通知等待的线程继续执行。Linux系统提供了pthread_cond_t类型来表示条件变量,并提供了相关的函数来操作条件变量。
下面是一个使用条件变量实现线程同步的示例:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define NUM_THREADS 5
int counter = 0;
pthread_mutex_t mutex;
pthread_cond_t cond;
void *thread_func(void *arg) {
pthread_mutex_lock(&mutex);
while (counter < 10) {
pthread_cond_wait(&cond, &mutex);
}
printf("Thread %d: Counter value is %d\n", *(int *)arg, counter);
pthread_mutex_unlock(&mutex);
return NULL;
}
int main() {
pthread_t threads[NUM_THREADS];
int thread_args[NUM_THREADS];
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
for (int i = 0; i < NUM_THREADS; i++) {
thread_args[i] = i;
pthread_create(&threads[i], NULL, thread_func, &thread_args[i]);
}
pthread_mutex_lock(&mutex);
counter = 10;
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
for (int i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
}
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
在上面的示例中,使用条件变量实现了线程的等待和唤醒机制。通过调用pthread_cond_wait函数在条件变量上等待,并且在其他线程中通过调用pthread_cond_broadcast函数广播条件满足,从而唤醒所有等待的线程。
3. 线程的调度
在Linux系统中,线程是由内核调度的,调度器根据一些策略决定应该运行哪个线程。线程的调度可以是抢占式的或者合作式的。
3.1 抢占式调度
在抢占式调度下,调度器可以在任意时刻中断当前运行的线程,并切换到另一个线程执行。这种调度方式可以保证线程在有限的时间内得到执行,避免了某个线程长时间独占CPU。
下面是一个使用抢占式调度的示例:
#include <stdio.h>
#include <pthread.h>
void *thread_func(void *arg) {
for (int i = 0; i < 100000; i++) {
printf("Thread %d: %d\n", *(int *)arg, i);
}
return NULL;
}
int main() {
pthread_t tid1, tid2;
int thread_arg1 = 1, thread_arg2 = 2;
pthread_create(&tid1, NULL, thread_func, &thread_arg1);
pthread_create(&tid2, NULL, thread_func, &thread_arg2);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}
在上面的示例中,创建了两个线程,并在每个线程中打印从0到99999的数。由于线程是并行执行的,并且调度以抢占式方式进行,所以两个线程会交替打印。
3.2 合作式调度
在合作式调度下,线程只有在主动放弃CPU的情况下,其他线程才能执行。每个线程负责控制自己的执行时间,如果某个线程不主动放弃CPU,则其他线程无法执行。
下面是一个使用合作式调度的示例:
#include <stdio.h>
#include <pthread.h>
void *thread_func(void *arg) {
for (int i = 0; i < 100000; i++) {
printf("Thread %d: %d\n", *(int *)arg, i);
pthread_yield(); // 主动放弃CPU
}
return NULL;
}
int main() {
pthread_t tid1, tid2;
int thread_arg1 = 1, thread_arg2 = 2;
pthread_create(&tid1, NULL, thread_func, &thread_arg1);
pthread_create(&tid2, NULL, thread_func, &thread_arg2);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}
在上面的示例中,创建了两个线程,并在每个线程中打印从0到99999的数。由于线程在每个迭代中都调用pthread_yield函数主动放弃CPU,所以两个线程能够交替打印。
4. 总结
本文介绍了Linux系统下的线程控制。线程是执行程序的最小单位,线程控制涉及线程的创建、运行和终止。使用pthread库可以方便地创建和管理线程。线程之间可以共享进程的资源,通过使用互斥量和条件变量等线程同步机制可以保证线程之间的协同工作。线程的调度可以是抢占式的或者合作式的,通过合理的调度可以提高程序的并发性能。