Administrator
Administrator
发布于 2024-09-05 / 2 阅读
0
0

第12章:多线程编程

12.1 多线程基础

1. 什么是线程

线程是进程中的一个执行单元,每个线程都共享进程的内存空间,但有自己独立的栈空间。使用多线程可以使程序同时执行多个任务。

2. POSIX线程(pthread)库

POSIX线程库是C语言中用于多线程编程的标准库。它提供了创建、管理、同步线程的API。

编译时链接pthread库:

gcc your_program.c -o your_program -lpthread

12.2 创建和管理线程

1. 创建线程

使用 pthread_create 函数创建一个新线程。

语法:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
  • thread:新线程的标识符。
  • attr:线程属性(通常为NULL,表示使用默认属性)。
  • start_routine:线程函数,即线程的入口函数。
  • arg:传递给线程函数的参数。

示例:

#include <stdio.h>
#include <pthread.h>

void *printMessage(void *arg) {
    printf("Hello from thread!\n");
    return NULL;
}

int main() {
    pthread_t thread; // 线程标识符

    // 创建线程
    if (pthread_create(&thread, NULL, printMessage, NULL) != 0) {
        printf("Error creating thread\n");
        return 1;
    }

    // 等待线程结束
    pthread_join(thread, NULL);
    printf("Thread has finished execution.\n");

    return 0;
}

2. 等待线程完成

使用 pthread_join 等待线程完成执行。

语法:

int pthread_join(pthread_t thread, void **retval);
  • thread:线程标识符。
  • retval:线程退出状态的指针(可以为NULL)。

12.3 线程同步

在多线程环境中,多个线程可能同时访问共享资源,导致数据不一致的问题。使用同步机制(如互斥锁、条件变量等)来防止这种情况。

1. 互斥锁

互斥锁(mutex)用于确保同一时刻只有一个线程访问共享资源。

相关函数:

  • pthread_mutex_init:初始化互斥锁。
  • pthread_mutex_lock:加锁。
  • pthread_mutex_unlock:解锁。
  • pthread_mutex_destroy:销毁互斥锁。

示例:

#include <stdio.h>
#include <pthread.h>

pthread_mutex_t lock;
int counter = 0;

void *incrementCounter(void *arg) {
    pthread_mutex_lock(&lock); // 加锁
    counter++;
    printf("Counter: %d\n", counter);
    pthread_mutex_unlock(&lock); // 解锁
    return NULL;
}

int main() {
    pthread_t threads[5];

    // 初始化互斥锁
    pthread_mutex_init(&lock, NULL);

    // 创建多个线程
    for (int i = 0; i < 5; i++) {
        pthread_create(&threads[i], NULL, incrementCounter, NULL);
    }

    // 等待所有线程完成
    for (int i = 0; i < 5; i++) {
        pthread_join(threads[i], NULL);
    }

    // 销毁互斥锁
    pthread_mutex_destroy(&lock);

    return 0;
}

2. 条件变量

条件变量(condition variable)用于使线程在某个条件成立前进入等待状态,并在条件成立时唤醒线程。

相关函数:

  • pthread_cond_init:初始化条件变量。
  • pthread_cond_wait:等待条件变量。
  • pthread_cond_signal:唤醒一个等待条件变量的线程。
  • pthread_cond_broadcast:唤醒所有等待条件变量的线程。
  • pthread_cond_destroy:销毁条件变量。

示例:

#include <stdio.h>
#include <pthread.h>

pthread_mutex_t lock;
pthread_cond_t cond;
int ready = 0;

void *waitForSignal(void *arg) {
    pthread_mutex_lock(&lock);
    while (!ready) {
        pthread_cond_wait(&cond, &lock); // 等待信号
    }
    printf("Thread received signal.\n");
    pthread_mutex_unlock(&lock);
    return NULL;
}

void *sendSignal(void *arg) {
    pthread_mutex_lock(&lock);
    ready = 1;
    pthread_cond_signal(&cond); // 发送信号
    pthread_mutex_unlock(&lock);
    return NULL;
}

int main() {
    pthread_t thread1, thread2;

    // 初始化互斥锁和条件变量
    pthread_mutex_init(&lock, NULL);
    pthread_cond_init(&cond, NULL);

    // 创建线程
    pthread_create(&thread1, NULL, waitForSignal, NULL);
    pthread_create(&thread2, NULL, sendSignal, NULL);

    // 等待线程完成
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    // 销毁互斥锁和条件变量
    pthread_mutex_destroy(&lock);
    pthread_cond_destroy(&cond);

    return 0;
}

12.4 线程属性

线程属性(pthread_attr_t)用于设置线程的行为,如分离状态、栈大小、调度策略等。

相关函数:

  • pthread_attr_init:初始化线程属性对象。
  • pthread_attr_setdetachstate:设置线程分离状态。
  • pthread_attr_destroy:销毁线程属性对象。

示例:设置线程为分离状态

#include <stdio.h>
#include <pthread.h>

void *threadFunction(void *arg) {
    printf("Hello from detached thread!\n");
    return NULL;
}

int main() {
    pthread_t thread;
    pthread_attr_t attr;

    // 初始化线程属性对象
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // 设置线程为分离状态

    // 创建分离线程
    pthread_create(&thread, &attr, threadFunction, NULL);

    // 销毁线程属性对象
    pthread_attr_destroy(&attr);

    // 主线程不需要等待分离线程
    printf("Main thread finished.\n");

    return 0;
}

12.5 线程安全

在多线程编程中,需要确保所有共享数据的访问都是线程安全的。使用互斥锁、条件变量和原子操作(如__sync函数)可以保证线程安全。

练习

  1. 编写一个程序,创建多个线程,每个线程递增一个共享计数器100次,并使用互斥锁确保线程安全。
  2. 编写一个生产者-消费者程序,使用条件变量和互斥锁来同步生产者和消费者线程的操作。
  3. 使用POSIX线程库编写一个简单的线程池,实现线程的复用和任务的分发。
  4. 编写一个程序,模拟一个简单的银行账户系统,允许多个线程同时执行存款和取款操作,确保数据一致性。

完成这些练习后,你将掌握C语言的多线程编程。


评论