第一部分:线程基本概念
一、线程简介
线程是操作系统能够进行运算调度的最小单位,它是一个进程内的独立控制流。线程之间共享同一进程的资源,如内存空间和其他系统资源。
二、线程的优势
- 效率高:由于线程共享相同的地址空间,因此线程之间的通信非常快速。
- 灵活性:线程可以并行执行任务,提高了应用程序的响应速度和性能。
- 资源节约:线程共享进程的资源,减少了资源开销。
第二部分:线程创建与管理
三、创建线程
在POSIX系统中,使用 pthread_create()
函数创建一个新的线程。此函数需要四个参数:线程标识符的引用、线程属性对象、线程入口点函数的指针、以及传递给线程函数的参数。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
void* threadFunction(void* arg) {
// 线程执行体
printf("Thread running\n");
return NULL;
}
int main() {
pthread_t thread;
if (pthread_create(&thread, NULL, threadFunction, NULL) == 0) {
printf("Thread created\n");
pthread_join(thread, NULL); // 等待线程结束
}
return 0;
}
四、线程属性
线程可以有不同的属性,如是否可以被取消、调度策略等。这些属性可以通过 pthread_attr_t
结构体设置。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
void* threadFunction(void* arg) {
printf("Thread running\n");
return NULL;
}
int main() {
pthread_t thread;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // 设置分离属性
if (pthread_create(&thread, &attr, threadFunction, NULL) == 0) {
printf("Thread created\n");
}
pthread_attr_destroy(&attr); // 销毁属性对象
return 0;
}
第三部分:线程同步
五、互斥锁 Mutex
互斥锁(Mutex)是用于保护临界区的最常见同步机制之一,它可以确保一次只有一个线程能够进入临界区。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
int counter = 0;
pthread_mutex_t mutex;
void* incrementCounter(void* arg) {
for (int i = 0; i < 1000000; ++i) {
pthread_mutex_lock(&mutex);
counter++;
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main() {
pthread_mutex_init(&mutex, NULL);
pthread_t threads[2];
for (int i = 0; i < 2; ++i) {
pthread_create(&threads[i], NULL, incrementCounter, NULL);
}
for (int i = 0; i < 2; ++i) {
pthread_join(threads[i], NULL);
}
printf("Final count: %d\n", counter);
pthread_mutex_destroy(&mutex);
return 0;
}
六、条件变量 Condition Variables
条件变量常用于线程间的协作,如生产者消费者模式中用于同步生产者和消费者的执行。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BUFFER_SIZE 10
int buffer[BUFFER_SIZE];
int head = 0;
int tail = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t full = PTHREAD_COND_INITIALIZER;
pthread_cond_t empty = PTHREAD_COND_INITIALIZER;
void* producer(void* arg) {
int value = 0;
while (1) {
pthread_mutex_lock(&mutex);
while ((head + 1) % BUFFER_SIZE == tail) {
pthread_cond_wait(&full, &mutex);
}
buffer[head] = value++;
head = (head + 1) % BUFFER_SIZE;
pthread_cond_signal(&empty);
pthread_mutex_unlock(&mutex);
usleep(1000);
}
return NULL;
}
void* consumer(void* arg) {
while (1) {
pthread_mutex_lock(&mutex);
while (head == tail) {
pthread_cond_wait(&empty, &mutex);
}
int value = buffer[tail];
tail = (tail + 1) % BUFFER_SIZE;
pthread_cond_signal(&full);
pthread_mutex_unlock(&mutex);
printf("Consumed: %d\n", value);
usleep(1000);
}
return NULL;
}
int main() {
pthread_t prod, cons;
pthread_create(&prod, NULL, producer, NULL);
pthread_create(&cons, NULL, consumer, NULL);
pthread_join(prod, NULL);
pthread_join(cons, NULL);
return 0;
}
第四部分:线程间通信
七、原子操作 Atomic Operations
原子操作可以避免多线程环境下的数据竞争问题。
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
atomic_int counter = ATOMIC_VAR_INIT(0);
void* incrementCounter(void* arg) {
for (int i = 0; i < 1000000; ++i) {
atomic_fetch_add_explicit(&counter, 1, memory_order_relaxed);
}
return NULL;
}
int main() {
pthread_t threads[2];
for (int i = 0; i < 2; ++i) {
pthread_create(&threads[i], NULL, incrementCounter, NULL);
}
for (int i = 0; i < 2; ++i) {
pthread_join(threads[i], NULL);
}
printf("Final count: %d\n", atomic_load_explicit(&counter, memory_order_relaxed));
return 0;
}
八、信号量 Semaphore
信号量可以用于解决线程间的同步问题。
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
sem_t semaphore;
void* waitOnSemaphore(void* arg) {
sem_wait(&semaphore);
printf("Thread got the semaphore\n");
sleep(1);
sem_post(&semaphore);
return NULL;
}
int main() {
sem_init(&semaphore, 0, 1);
pthread_t threads[2];
for (int i = 0; i < 2; ++i) {
pthread_create(&threads[i], NULL, waitOnSemaphore, NULL);
}
for (int i = 0; i < 2; ++i) {
pthread_join(threads[i], NULL);
}
sem_destroy(&semaphore);
return 0;
}
第五部分:高级应用
九、线程池 ThreadPool
线程池是一种常见的模式,用于管理一组可复用的线程。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define NUM_THREADS 4
pthread_mutex_t queueLock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t queueNotEmpty = PTHREAD_COND_INITIALIZER;
int queue[100];
int head = 0;
int tail = 0;
typedef struct {
int id;
} job_t;
void* worker(void* arg) {
while (1) {
pthread_mutex_lock(&queueLock);
while (head == tail) {
pthread_cond_wait(&queueNotEmpty, &queueLock);
}
job_t job = queue[tail];
tail = (tail + 1) % 100;
pthread_mutex_unlock(&queueLock);
printf("Thread %d is working on job %d\n", *((int*)arg), job.id);
sleep(1);
}
return NULL;
}
void addJob(job_t job) {
pthread_mutex_lock(&queueLock);
queue[head] = job;
head = (head + 1) % 100;
pthread_cond_signal(&queueNotEmpty);
pthread_mutex_unlock(&queueLock);
}
int main() {
pthread_t workers[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; ++i) {
pthread_create(&workers[i], NULL, worker, &i);
}
for (int i = 0; i < 10; ++i) {
job_t job = {i};
addJob(job);
}
for (int i = 0; i < NUM_THREADS; ++i) {
pthread_join(workers[i], NULL);
}
pthread_mutex_destroy(&queueLock);
pthread_cond_destroy(&queueNotEmpty);
return 0;
}
第六部分:线程生命周期管理
十、线程终止
线程可以通过 pthread_exit()
或者接收到某些信号如 SIGTERM
来终止。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
void* threadFunction(void* arg) {
printf("Thread running\n");
pthread_exit(NULL);
}
int main() {
pthread_t thread;
if (pthread_create(&thread, NULL, threadFunction, NULL) == 0) {
printf("Thread created\n");
pthread_join(thread, NULL); // 等待线程结束
}
return 0;
}
十一、线程分离 Detaching Threads
线程可以被分离(detached),这意味着线程结束后不需要其他线程显式地调用 pthread_join()
。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
void* threadFunction(void* arg) {
printf("Thread running\n");
pthread_exit(NULL);
}
int main() {
pthread_t thread;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (pthread_create(&thread, &attr, threadFunction, NULL) == 0) {
printf("Thread created\n");
}
pthread_attr_destroy(&attr);
return 0;
}
十二、线程取消 Cancellation
线程可以通过 pthread_cancel()
来取消,而被取消的线程需要通过 pthread_cleanup_push()
和 pthread_cleanup_pop()
来处理清理工作。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
void cleanupFunction(void* arg) {
printf("Cleaning up...\n");
}
void* cancellableThread(void* arg) {
pthread_cleanup_push(cleanupFunction, NULL);
for (int i = 0; i < 1000000; ++i) {
if (pthread_testcancel() == 0) {
printf("Working...\n");
}
}
pthread_cleanup_pop(0);
return NULL;
}
int main() {
pthread_t thread;
pthread_create(&thread, NULL, cancellableThread, NULL);
sleep(1); // 让线程运行一会儿
pthread_cancel(thread);
pthread_join(thread, NULL);
return 0;
}
第七部分:线程同步与并发控制
十三、读写锁 Reader-Writer Locks
读写锁允许多个读线程同时访问资源,但只允许一个写线程访问资源。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
pthread_rwlock_t lock;
int counter = 0;
void* incrementCounter(void* arg) {
for (int i = 0; i < 1000000; ++i) {
pthread_rwlock_wrlock(&lock);
counter++;
pthread_rwlock_unlock(&lock);
}
return NULL;
}
void* readCounter(void* arg) {
for (int i = 0; i < 1000000; ++i) {
pthread_rwlock_rdlock(&lock);
int value = counter;
pthread_rwlock_unlock(&lock);
}
return NULL;
}
int main() {
pthread_rwlock_init(&lock, NULL);
pthread_t threads[4];
for (int i = 0; i < 2; ++i) {
pthread_create(&threads[i], NULL, incrementCounter, NULL);
}
for (int i = 2; i < 4; ++i) {
pthread_create(&threads[i], NULL, readCounter, NULL);
}
for (int i = 0; i < 4; ++i) {
pthread_join(threads[i], NULL);
}
printf("Final count: %d\n", counter);
pthread_rwlock_destroy(&lock);
return 0;
}
第八部分:线程相关API接口说明
1. pthread_create()
- 功能:创建一个新的线程。
- 参数:
pthread_t *restrict thread
: 线程标识符的引用。const pthread_attr_t *restrict attr
: 线程属性对象。void *(*start_routine)(void *)
: 线程的入口点函数。void *restrict arg
: 传递给线程函数的参数。
- 返回值:如果成功返回0,否则返回错误码。
2. pthread_join()
- 功能:等待一个线程结束。
- 参数:
pthread_t thread
: 要等待的线程的标识符。void **retval
: 如果线程正常退出,则会存放线程函数的返回值。
- 返回值:如果成功返回0,否则返回错误码。
3. pthread_detach()
- 功能:分离一个线程。
- 参数:
pthread_t thread
: 要分离的线程的标识符。
- 返回值:如果成功返回0,否则返回错误码。
4. pthread_mutex_lock() 和 pthread_mutex_unlock()
- 功能:分别锁定和解锁一个互斥锁。
- 参数:
pthread_mutex_t *mutex
: 互斥锁对象。
- 返回值:如果成功返回0,否则返回错误码。
5. pthread_rwlock_wrlock() 和 pthread_rwlock_unlock()
- 功能:分别锁定和解锁一个读写锁(写入模式)。
- 参数:
pthread_rwlock_t *rwlock
: 读写锁对象。
- 返回值:如果成功返回0,否则返回错误码。
6. pthread_rwlock_rdlock() 和 pthread_rwlock_unlock()
- 功能:分别锁定和解锁一个读写锁(读取模式)。
- 参数:
pthread_rwlock_t *rwlock
: 读写锁对象。
- 返回值:如果成功返回0,否则返回错误码。
7. pthread_cancel()
- 功能:请求取消指定的线程。
- 参数:
pthread_t thread
: 要取消的线程的标识符。
- 返回值:如果成功返回0,否则返回错误码。
8. pthread_testcancel()
- 功能:测试线程是否已经被请求取消。
- 返回值:无返回值,如果线程被请求取消,则会抛出异常。
9. pthread_cleanup_push() 和 pthread_cleanup_pop()
- 功能:用于在取消点之前注册清理函数。
- 参数:
void (*routine)(void *)
: 清理函数。void *arg
: 传递给清理函数的参数。
总结
本文详细介绍了线程的基本概念、创建与管理、同步机制、线程间通信、高级应用等多个方面,并提供了丰富的实战示例代码以及对常用线程API接口的说明。通过学习这些知识,你将能够在实际项目中更有效地管理和利用多线程架构。