【快速上手C语言】第十三章:探索C语言中的并发编程-多线程、同步与嵌入式实践

        随着计算机硬件的多核化发展,并发编程在现代软件开发中变得越来越重要。C语言,作为一种高效且接近硬件的编程语言,提供了丰富的工具来处理并发任务。本章将深入探讨C语言中的并发编程,主要涉及多线程编程(POSIX线程)、线程同步与互斥、线程的创建与管理以及嵌入式系统中的并发编程实践。

一、多线程编程(POSIX线程)

1. 什么是POSIX线程?

        POSIX线程(Pthreads)是一个在Unix、Linux和其他POSIX兼容系统上实现多线程编程的标准。Pthreads库提供了一组API,用于在C语言中创建和控制线程。这些线程可以并行执行,从而更好地利用多核处理器的能力。

2. 线程的基本概念

        一个线程是一个轻量级的进程,它具有自己的栈和程序计数器,但与同一进程中的其他线程共享全局变量和堆。使用多线程可以加速计算任务,提高应用程序的响应速度。

3. 创建和终止线程

pthread_create 用于创建新线程,pthread_exit 用于终止线程。

示例代码:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

void *thread_function(void *arg) {
    int *num = (int *)arg;
    printf("线程ID: %ld, 参数: %d\n", pthread_self(), *num);
    pthread_exit(NULL);
}

int main() {
    pthread_t thread;
    int arg = 42;
    
    if (pthread_create(&thread, NULL, thread_function, &arg) != 0) {
        fprintf(stderr, "线程创建失败\n");
        return 1;
    }

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

    printf("主线程结束\n");
    return 0;
}
# 运行结果:
# 线程ID: 140706795197184, 参数: 42
# 主线程结束

二、线程同步与互斥

        在多线程编程中,多个线程可能会同时访问共享资源,如变量或数据结构。如果不加以控制,可能会导致竞争条件和数据不一致的问题。为了解决这些问题,C语言提供了线程同步和互斥机制。

1. 互斥锁(Mutex)

        互斥锁用于确保同一时刻只有一个线程可以访问共享资源。它通过 pthread_mutex_t 类型和相关函数实现。

示例代码:
#include <pthread.h>
#include <stdio.h>

pthread_mutex_t mutex;
int counter = 0;

void *increment_counter(void *arg) {
    for (int i = 0; i < 1000000; i++) {
        pthread_mutex_lock(&mutex);
        counter++;
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main() {
    pthread_t thread1, thread2;

    pthread_mutex_init(&mutex, NULL);

    pthread_create(&thread1, NULL, increment_counter, NULL);
    pthread_create(&thread2, NULL, increment_counter, NULL);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    printf("最终计数器值: %d\n", counter);

    pthread_mutex_destroy(&mutex);
    return 0;
}
# 运行结果:
# 最终计数器值: 2000000

在这个例子中,两个线程通过互斥锁确保在每次递增计数器时不会发生竞态条件。

2. 信号量(Semaphore)

        信号量是一种更高级的同步机制,可以控制多个线程对共享资源的访问。它适用于控制特定数量的资源,如限制线程池中活跃线程的数量。

示例代码:
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>

sem_t semaphore;
int counter = 0;

void *worker(void *arg) {
    sem_wait(&semaphore);
    counter++;
    printf("线程ID: %ld, 计数器值: %d\n", pthread_self(), counter);
    sem_post(&semaphore);
    return NULL;
}

int main() {
    pthread_t threads[5];

    sem_init(&semaphore, 0, 3); // 允许同时最多3个线程访问

    for (int i = 0; i < 5; i++) {
        pthread_create(&threads[i], NULL, worker, NULL);
    }

    for (int i = 0; i < 5; i++) {
        pthread_join(threads[i], NULL);
    }

    sem_destroy(&semaphore);
    return 0;
}
# 运行结果:
# 线程ID: 140705128797952, 计数器值: 1
# 线程ID: 140705137190656, 计数器值: 2
# 线程ID: 140705145583360, 计数器值: 3
# 线程ID: 140705153976064, 计数器值: 4
# 线程ID: 140705162368768, 计数器值: 5

3. 条件变量(Condition Variable)

        条件变量用于线程之间的通知机制,一个线程等待某个条件成立,另一个线程通知其条件已成立。常用于生产者-消费者模型中。

示例代码:
#include <pthread.h>
#include <stdio.h>

pthread_mutex_t mutex;
pthread_cond_t cond;
int ready = 0;

void *producer(void *arg) {
    pthread_mutex_lock(&mutex);
    ready = 1;
    printf("生产者: 准备好了!\n");
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);
    return NULL;
}

void *consumer(void *arg) {
    pthread_mutex_lock(&mutex);
    while (!ready) {
        pthread_cond_wait(&cond, &mutex);
    }
    printf("消费者: 收到信号,开始处理!\n");
    pthread_mutex_unlock(&mutex);
    return NULL;
}

int main() {
    pthread_t prod, cons;

    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);

    pthread_create(&cons, NULL, consumer, NULL);
    pthread_create(&prod, NULL, producer, NULL);

    pthread_join(prod, NULL);
    pthread_join(cons, NULL);

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    return 0;
}
# 运行结果:
# 生产者: 准备好了!
# 消费者: 收到信号,开始处理!

三、线程的创建与管理

1. 线程属性

pthread_attr_t 用于设置线程属性,例如是否为分离状态(detached)、栈大小等。设置分离状态的线程在终止时会自动释放资源。

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

void *thread_function(void *arg) {
    printf("分离线程在运行\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, thread_function, NULL) != 0) {
        fprintf(stderr, "线程创建失败\n");
        return 1;
    }

    pthread_attr_destroy(&attr);

    printf("主线程结束,但分离线程仍然在运行\n");
    return 0;
}

2. 线程的取消与清理

pthread_cancel 可用于请求取消一个线程,pthread_cleanup_pushpthread_cleanup_pop 用于定义清理函数,在线程退出时自动调用。

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

void cleanup(void *arg) {
    printf("清理资源: %s\n", (char *)arg);
}

void *thread_function(void *arg) {
    pthread_cleanup_push(cleanup, "线程清理");
    while (1) {
        printf("线程在运行...\n");
        sleep(1);
    }
    pthread_cleanup_pop(1);
    pthread_exit(NULL);
}

int main() {
    pthread_t thread;
    pthread_create(&thread, NULL, thread_function, NULL);

    sleep(3);
    printf("取消线程...\n");
    pthread_cancel(thread);

    pthread_join(thread, NULL);
    return 0;
}
# 运行结果:
# 线程在运行...
# 线程在运行...
# 线程在运行...
# 取消线程...
# 清理资源: 线程清理

四、嵌入式系统中的并发编程实践

        在嵌入式系统中,并发编程通常用于处理多任务操作,如传感器数据采集、通信协议栈处理、用户界面刷新等。由于嵌入式系统资源有限,必须谨慎管理线程,以确保系统的稳定性和实时性。

1. 实时性与资源管理

        嵌入式系统的并发编程需要特别关注实时性。选择适当的调度策略(如优先级调度)和适时的任务切换非常重要。同时,由于内存和CPU资源有限,创建过多线程可能导致系统性能下降。因此,通常采用任务调度器或轻量级线程库,如FreeRTOS。

2. 实践示例:多任务处理

假设我们需要在嵌入式系统中实现传感器数据采集和处理的任务,可以通过多线程的方式来实现。

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

void *sensor_read(void *arg) {
    while (1) {
        printf("采集传感器数据...\n");
        sleep(2);  // 模拟传感器数据采集时间
    }
    return NULL;
}

void *data_process(void *arg) {
    while (1) {
        printf("处理数据...\n");
        sleep(3);  // 模拟数据处理时间
    }
    return NULL;
}

int main() {
    pthread_t sensor_thread, process_thread;

    pthread_create(&sensor_thread, NULL, sensor_read, NULL);
    pthread_create(&process_thread, NULL, data_process, NULL);

    pthread_join(sensor_thread, NULL);
    pthread_join(process_thread, NULL);

    return 0;
}
# 运行结果:
# 采集传感器数据...
# 处理数据...
# 采集传感器数据...
# 处理数据...
# 采集传感器数据...

        在这个示例中,我们创建了两个线程,一个负责传感器数据采集,另一个负责数据处理。这种结构使得数据采集和处理可以并行进行,提高了系统的响应速度。

总结

        并发编程是现代C语言开发中不可或缺的一部分,尤其是在多核处理器和嵌入式系统中。通过学习和掌握多线程编程(POSIX线程)、线程同步与互斥机制、线程的创建与管理以及嵌入式系统中的并发编程实践,开发者可以创建高效、稳定且响应迅速的应用程序。 然而,并发编程也带来了复杂性,如竞态条件、死锁和资源管理等问题。理解并正确使用这些工具,将有助于在实际开发中更好地管理并发任务,提升软件的质量和性能。

  • 12
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值