多线程编程

多线程编程

pthread_create 创建线程
#include 

int pthread_create(
    pthread_t *thread,          // 线程标识符(输出参数)
    const pthread_attr_t *attr, // 线程属性(通常设为 NULL)
    void *(*start_routine)(void *), // 线程入口函数
    void *arg                  // 传递给线程函数的参数
);

//成功返回 0,失败返回错误码(非 errno,需用 strerror 解析)
//编译时需要加上 -pthread选项
参数 说明
thread 指向 pthread_t 的指针,用于存储新线程的 ID。
attr 线程属性(如栈大小、调度策略等),通常设为 NULL(默认属性)。
start_routine 线程的入口函数,必须是 void* func(void*) 形式。
arg 传递给 start_routine 的参数(可以是任意类型,需强制转换),可填NULL。
示例
//单线程

#include 
#include 
#include 

// 线程函数
void* thread_task(void *arg) {
	int num = *(int *)arg;
	printf("这是分支线程%d\n", num);
	sleep(1);
	return NULL;
}

int main() {
	pthread_t tid;
	int num = 42;
	
	// 创建线程
	if (pthread_create(&tid, NULL, thread_task, &num) != 0) {
		perror("pthread_create failed");
		return 1;
	}
	
	
	sleep(1);// 等待分支线程结束
	printf("Main thread exits\n");
	return 0;
}

/*
	输出:
	这是分支线程42
	Main thread exits
*/

//多线程
#include 
#include 
#include 

#define NUM_THREADS 5

void* thread_func(void *arg) {
    int id = *(int *)arg;
    printf("Thread %d is running\n", id);
    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;
        if (pthread_create(&threads[i], NULL, thread_func, &thread_args[i]) != 0) {
            perror("pthread_create failed");
            return 1;
        }
    }

	sleep(2);//等待分支进程结束

    printf("All threads finished\n");
    return 0;
}

/*
	输出:
	Thread 0 is running
	Thread 1 is running
	Thread 2 is running
	Thread 3 is running
	Thread 4 is running
	All threads finished

*/

//向线程中传入多个数据
#include 
#include 

// 定义信息结构体,用于向线程传递数据
typedef struct {
    int num;
    char name[20];
    double score;
} Info;

// 线程函数
void *task(void *arg) {
    Info buf = *((Info*)arg); // 解引用获取结构体副本
    printf("子线程中:num = %d, name = %s, score = %.2lf\n", 
           buf.num, buf.name, buf.score);
    return NULL;
}

int main(int argc, const char *argv[]) {
    pthread_t tid;
    Info buf = {
        .num = 520,
        .name = "zhangsan",
        .score = 99.5
    };

    // 创建线程
    if (pthread_create(&tid, NULL, task, &buf) != 0) {
        perror("线程创建失败");
        return 1;
    }

    printf("线程创建成功,tid = 0x%lx\n", (unsigned long)tid);
    printf("主线程运行中...\n");

    // 等待子线程结束(避免主线程直接退出)
    sleep(2);
    
    return 0;
}
pthread_self 获取线程号
#include  
pthread_t pthread_self(void);
//功能:获取当前线程的线程号 
//参数:无 
//返回值:返回调用线程的id号,不会失败
pthread_exit 线程退出
#include 
void pthread_exit(void *retval);
//功能:退出当前线程 
//参数:表示退出时的状态,一般填NULL 
//返回值:无
pthread_join 线程资源回收
#include 
int pthread_join(pthread_t thread, void **retval);
/*
功能:阻塞回收指定线程的资源 
参数1:要回收的线程线程号 
参数2:线程退出时的状态,一般填NULL 
返回值:成功返回0,失败返回一个错误码
*/
pthread_detach 线程分离态
#include 
int pthread_detach(pthread_t thread); 
/*
功能:将指定线程设置成分离态,被设置成分离态的线程,退出后,资源由系统自动回收 
参数:要分离的线程号 
返回值:成功返回0,失败返回一个错误码
*/

//示例
#include 
#include 
#include 

// 线程函数
void* task(void* arg) {
    printf("子线程 ID: 0x%lx\n", (unsigned long)pthread_self());
    sleep(3);
    pthread_exit(nullptr);  // 显式退出线程
}

int main() {
    pthread_t tid;
    
    // 创建线程
    if (pthread_create(&tid, nullptr, task, nullptr) != 0) {
        perror("线程创建失败");
        return EXIT_FAILURE;
    }

    printf("主线程中 - 子线程 ID: 0x%lx\n", (unsigned long)tid);

    // 设置线程为分离状态(非阻塞)
    if (pthread_detach(tid) != 0) {
        perror("线程分离失败");
        return EXIT_FAILURE;
    }

    sleep(5);  // 主线程等待足够时间让子线程完成
    
    std::cout << "主线程结束" << std::endl;
    return EXIT_SUCCESS;
}
案例
//使用多线程完成两个文件的拷贝,线程1拷贝前一半内容,线程2拷贝后一半内容,主线程用于回收两个分支线程的资源
#include 
#include 
#include 
#include 
#include 
#include 

// 定义要向线程体函数中传递数据的结构体类型
struct Info {
    const char *srcfile;  // 要拷贝的原文件
    const char *destfile; // 目标文件
    int start;           // 起始位置
    int len;             // 要拷贝的长度
};

// 定义获取文件长度的函数
int get_file_len(const char *srcfile, const char *destfile)
{
    // 定义两个文件描述符,分别作为源文件和目标文件的句柄
    int sfd, dfd;
    
    // 以只读的形式打开源文件
    if ((sfd = open(srcfile, O_RDONLY)) == -1) {
        perror("open srcfile error");
        return -1;
    }
    
    // 以只写的形式打开目标文件
    if ((dfd = open(destfile, O_RDWR|O_CREAT|O_TRUNC, 0664)) == -1) {
        perror("open destfile error");
        return -1;
    }
    
    // 获取源文件的长度
    int len = lseek(sfd, 0, SEEK_END);
    
    // 关闭文件
    close(sfd);
    close(dfd);
    
    return len;
}

// 定义线程体函数
void *task(void *arg)
{
    // 将传入的数据解析出来
    const char *srcfile = ((struct Info*)arg)->srcfile;
    const char *destfile = ((struct Info*)arg)->destfile;
    int start = ((struct Info*)arg)->start;
    int len = ((struct Info*)arg)->len;
    
    // 准备拷贝工作
    // 定义两个文件描述符,分别作为源文件和目标文件的句柄
    int sfd, dfd;
    
    // 以只读的形式打开源文件
    if ((sfd = open(srcfile, O_RDONLY)) == -1) {
        perror("open srcfile error");
        return NULL;
    }
    
    // 以只写的形式打开目标文件
    if ((dfd = open(destfile, O_RDWR)) == -1) {
        perror("open destfile error");
        return NULL;
    }
    
    // 偏移指针
    lseek(sfd, start, SEEK_SET);
    lseek(dfd, start, SEEK_SET);
    
    // 拷贝工作
    int ret = 0;         // 记录每次读取的数据
    int count = 0;       // 记录拷贝的总个数
    char buf[128] = "";  // 数据搬运工
    
    while (1) {
        ret = read(sfd, buf, sizeof(buf));
        // 将读取的数据放入到count中
        count += ret;
        
        if (count >= len) {
            // 说明该部分的内容拷贝结束,还剩最后一次
            write(dfd, buf, ret - (count - len));
            break;
        }
        
        // 其余的正常拷贝
        write(dfd, buf, ret);
    }
    
    // 关闭文件描述符
    close(dfd);
    close(sfd);
}

int main(int argc, const char *argv[])
{
    // 判断传入的文件个数是否正确
    if (argc != 3) {
        printf("input file error\n");
        printf("usage:./a.out srcfile destfile\n");
        return -1;
    }
    
    // 获取原文件的长度,顺便将目标文件创建出来
    int len = get_file_len(argv[1], argv[2]);
    
    // 创建两个线程
    pthread_t tid1, tid2;
    
    // 定义向线程体函数传参的变量
    struct Info buf[2] = {
        {argv[1], argv[2], 0, len/2},
        {argv[1], argv[2], len/2, len - len/2}
    };
    
    if (pthread_create(&tid1, NULL, task, &buf[0]) != 0) {
        printf("线程创建失败\n");
        return -1;
    }
    
    if (pthread_create(&tid2, NULL, task, &buf[1]) != 0) {
        printf("线程创建失败\n");
        return -1;
    }
    
    // 主线程中完成对两个分支线程资源的回收
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    
    printf("拷贝成功\n");
    std::cout << "Hello, World!" << std::endl;
    
    return 0;
}

线程的同步互斥机制

一、为什么需要同步互斥?

当多个线程共享资源时,可能出现:

  1. 竞态条件(Race Condition):执行结果依赖于线程执行顺序
  2. 数据不一致:并发访问导致数据状态异常
  3. 死锁(Deadlock):线程互相等待对方释放资源
二、主要同步互斥机制
互斥锁(Mutex)
  • 互斥锁的本质也是一个特殊的临界资源,当该临界资源被某个线程所拥有后,其他线程就不能拥有该资源,直到,拥有该资源的线程释放掉互斥锁后,其他线程才能进行抢占(同一时刻,一个互斥锁只能被一个线程所拥有)

  • 基本用法:

    //创建互斥锁:定义一个个pthread_mutex_t 类型的变量即创建了一个互斥锁
    //初始化互斥锁
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//静态初始化
    
    int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
    /*
    	功能:初始化互斥锁变量 
    	参数1:互斥锁变量的地址,属于地址传递 
    	参数2:互斥锁属性,一般填NULL,让系统自动设置互斥锁属性 
    	返回值:成功返回0,失败返回错误码
    */
    
    //获取锁资源
    int pthread_mutex_lock(pthread_mutex_t *mutex); /*
    	功能:获取锁资源,如果要获取的互斥锁已经被其他线程锁定,那么该函数会阻塞,直到能够获取锁资源 
    	参数:互斥锁地址,属于地址传递
        返回值:成功返回0,失败返回一个错误码
    */
    
    //释放锁资源
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
    /*
    	功能:释放对互斥锁资源的拥有权 
    	参数:互斥锁变量的地址 
    	返回值:成功返回0,失败返回一个错误码
    */
    
    //销毁互斥锁
    int pthread_mutex_destroy(pthread_mutex_t *mutex);
    /*
    	功能:销毁互斥锁 
    	参数:互斥锁变量的地址 
    	返回值:成功返回0,失败返回一个错误码
    */
    
  • 特点:

    • 同一时刻只允许一个线程进入临界区
    • 阻塞式等待
    • 可能造成死锁(需避免嵌套加锁)
  • 示例:

    #include 
    #include 
    #include 
    
    // 全局互斥锁
    pthread_mutex_t mutex;
    
    // 共享资源
    int num = 520;
    
    // 线程1:每次减少10
    void* task1(void* arg) {
       while (1) {
           sleep(1);
           
           pthread_mutex_lock(&mutex);
           num -= 10;
           printf("张三取了10,剩余%d\n", num);
           pthread_mutex_unlock(&mutex);
       }
       return nullptr;
    }
    
    // 线程2:每次减少20
    void* task2(void* arg) {
       while (1) {
           sleep(1);
           
           pthread_mutex_lock(&mutex);
           num -= 20;
           printf("李四取了20,剩余%d\n", num);
           pthread_mutex_unlock(&mutex);
       }
       return nullptr;
    }
    
    int main() {
       // 初始化互斥锁
       pthread_mutex_init(&mutex, nullptr);
    
       // 创建线程
       pthread_t tid1, tid2;
       if (pthread_create(&tid1, nullptr, task1, nullptr) != 0) {
           std::cerr << "线程1创建失败" << std::endl;
           return 1;
       }
       if (pthread_create(&tid2, nullptr, task2, nullptr) != 0) {
           std::cerr << "线程2创建失败" << std::endl;
           return 1;
       }
    
       printf("主线程:tid1 = 0x%lx, tid2 = 0x%lx\n", 
             (unsigned long)tid1, (unsigned long)tid2);
    
       // 等待线程结束(实际不会执行到,因线程是无限循环)
       pthread_join(tid1, nullptr);
       pthread_join(tid2, nullptr);
    
       // 销毁互斥锁
       pthread_mutex_destroy(&mutex);
    
       std::cout << "程序结束" << std::endl;
       return 0;
    }
    
    /*
    输出:
    主线程:tid1 = 0x2, tid2 = 0x3
    张三取了10,剩余510
    李四取了20,剩余490
    张三取了10,剩余480
    李四取了20,剩余460
    张三取了10,剩余450
    李四取了20,剩余430
    张三取了10,剩余420
    李四取了20,剩余400
    张三取了10,剩余390
    李四取了20,剩余370
    张三取了10,剩余360
    ...
    */
    
读写锁(Read-Write Lock)
  • 概念

    读写锁是一种特殊的同步机制,它允许多个线程同时读取共享资源,但只允许一个线程写入资源,非常适合读多写少的场景。

    1. 三种状态
      • 读模式加锁(共享)
      • 写模式加锁(独占)
      • 未加锁
    2. 优先级策略
      • 默认情况下,读写锁不保证读和写的优先级
      • 某些实现支持写优先或读优先策略
  • 基本用法:

    //初始化
    pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;  // 静态初始化
    
    pthread_rwlock_init(&rwlock, NULL);//动态初始化
    /*
    	功能:初始化锁变量 
    	参数1:锁变量的地址,属于地址传递 
    	参数2:锁属性,一般填NULL,让系统自动设置互斥锁属性 
    	返回值:成功返回0,失败返回错误码
    */
    
    //读锁定(共享锁)
    pthread_rwlock_rdlock(&rwlock);  // 阻塞获取读锁
    pthread_rwlock_tryrdlock(&rwlock);  // 非阻塞尝试获取读锁
    
    //写锁定(独占锁)
    pthread_rwlock_wrlock(&rwlock);  // 阻塞获取写锁
    pthread_rwlock_trywrlock(&rwlock);  // 非阻塞尝试获取写锁
    
    //解锁
    pthread_rwlock_unlock(&rwlock);  // 释放读写锁
    
    //销毁
    pthread_rwlock_destroy(&rwlock);
    
  • 特点:

    • 多个读线程可并发访问
    • 写线程独占访问
    • 适合读多写少的场景
  • 示例

    #include 
    #include 
    #include 
    #include 
    
    pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
    std::vector<int> shared_data;  // 共享数据
    
    void* reader(void* arg) {
    	int id = *(int*)arg;  // 获取读者 ID
    	while (true) {
    		pthread_rwlock_rdlock(&rwlock);
    		
    		std::cout << "Reader " << id << " sees: ";
    		for (int num : shared_data) {
    			std::cout << num << " ";
    		}
    		std::cout << std::endl;
    		
    		pthread_rwlock_unlock(&rwlock);
    		sleep(1);
    	}
    	return nullptr;
    }
    
    void* writer(void* arg) {
    	int id = *(int*)arg;  // 获取写者 ID
    	int count = 0;
    	while (true) {
    		pthread_rwlock_wrlock(&rwlock);
    		
    		shared_data.push_back(++count);
    		std::cout << "Writer " << id << " added " << count << std::endl;
    		
    		pthread_rwlock_unlock(&rwlock);
    		sleep(2);
    	}
    	return nullptr;
    }
    
    int main() {
    	const int NUM_READERS = 3;
    	const int NUM_WRITERS = 1;
    	pthread_t readers[NUM_READERS], writers[NUM_WRITERS];
    	int readers_id[NUM_READERS],writers_id[NUM_WRITERS];
    	
    	// 创建读者线程
    	for (int i = 0; i < NUM_READERS; ++i) {
    		readers_id[i] = i;
    		pthread_create(&readers[i], NULL, reader, &readers_id[i]);
    	}
    	
    	// 创建写者线程
    	for (int i = 0; i < NUM_WRITERS; ++i) {
    		writers_id[i] = i;
    		pthread_create(&writers[i], NULL, writer, &writers_id[i]);
    	}
    	
    	// 等待线程
    	for (int i = 0; i < NUM_READERS; ++i) {
    		pthread_join(readers[i], NULL);
    	}
    	for (int i = 0; i < NUM_WRITERS; ++i) {
    		pthread_join(writers[i], NULL);
    	}
    	
    	pthread_rwlock_destroy(&rwlock);
    	return 0;
    }
    
    
条件变量(Condition Variable)
  • 概念:条件变量是线程同步的重要机制,它与互斥锁配合使用可以实现复杂的线程同步需求:

    1. 核心作用:允许线程在某个条件不满足时主动休眠,直到被其他线程唤醒
    2. 典型应用:生产者-消费者模型、工作队列等场景
    3. 关键特点
      • 维护一个等待队列
      • 必须与互斥锁配合使用
      • 提供广播(broadcast)和单播(signal)两种唤醒方式
  • 基本用法:

    //定义和初始化
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;// 静态初始化(推荐)
    
    pthread_cond_t cond;
    pthread_cond_init(&cond, NULL);// 动态初始化
    
    int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
    /*
    	功能:初始化条件变量
        参数1:条件变量的起始地址 
        参数2:条件变量的属性,一般填NULL 
        返回值:成功返回0,失败返回一个错误码
    */
    
    //等待条件
    int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
    /*
    	功能:将线程放入休眠等待队列,等待其他线程的唤醒 
    	参数1:条件变量的地址 
    	参数2:互斥锁,由于多个消费者线程进入等待队列时会产生竞态,为了解决竞态,需要使用一个互斥锁 
    	返回值:成功返回0,失败返回错误码
    */
    
    //通知条件
    int pthread_cond_broadcast(pthread_cond_t *cond);
    /*
    	功能:唤醒条件变量维护的队列中的所有消费者线程 
    	参数:条件变量的地址 
    	返回值:成功返回0,失败返回错误码
    */
    int pthread_cond_signal(pthread_cond_t *cond);
    /*
    	功能:唤醒条件变量维护的队列中的第一个进入队列的消费者线程 
    	参数:条件变量的地址 
    	返回值:成功返回0,失败返回错误码
    */
    
    //销毁条件变量
    int pthread_cond_destroy(pthread_cond_t *cond);
    /*
    	功能:销毁一个条件变量 
    	参数:条件变量的地址
        返回值:成功返回0,失败返回错误码
    */
    
  • 特点:

    • 必须与互斥锁配合使用
    • 解决"生产者-消费者"问题
    • 避免忙等待(busy-waiting)
  • 示例

    #include 
    #include 
    #include 
    
    // 定义条件变量和互斥锁
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    
    // 生产者线程
    void* producer(void* arg) {
        sleep(3);  // 模拟生产过程耗时
        std::cout << "生产者(" << pthread_self() << "): 生产了3辆小米su7" << std::endl;
        
        // 唤醒所有消费者线程
        pthread_cond_broadcast(&cond);
        
        pthread_exit(nullptr);
    }
    
    // 消费者线程
    void* consumer(void* arg) {
        // 获取互斥锁
        pthread_mutex_lock(&mutex);
        
        // 等待条件变量通知
        pthread_cond_wait(&cond, &mutex);
        std::cout << "消费者(" << pthread_self() << "): 消费了一辆小米su7,很开心" << std::endl;
        
        // 释放互斥锁
        pthread_mutex_unlock(&mutex);
        
        pthread_exit(nullptr);
    }
    
    int main() {
        // 创建线程
        pthread_t tid_producer, tid_consumer1, tid_consumer2, tid_consumer3;
        
        // 创建1个生产者线程
        if (pthread_create(&tid_producer, nullptr, producer, nullptr) != 0) {
            std::cerr << "生产者线程创建失败" << std::endl;
            return EXIT_FAILURE;
        }
        
        // 创建3个消费者线程
        if (pthread_create(&tid_consumer1, nullptr, consumer, nullptr) != 0 ||
            pthread_create(&tid_consumer2, nullptr, consumer, nullptr) != 0 ||
            pthread_create(&tid_consumer3, nullptr, consumer, nullptr) != 0) {
            std::cerr << "消费者线程创建失败" << std::endl;
            return EXIT_FAILURE;
        }
    
        // 打印线程ID
        std::cout << "主线程: " 
                  << "生产者=" << tid_producer 
                  << ", 消费者1=" << tid_consumer1
                  << ", 消费者2=" << tid_consumer2
                  << ", 消费者3=" << tid_consumer3 << std::endl;
    
        // 等待所有线程结束
        pthread_join(tid_producer, nullptr);
        pthread_join(tid_consumer1, nullptr);
        pthread_join(tid_consumer2, nullptr);
        pthread_join(tid_consumer3, nullptr);
    
        // 销毁条件变量和互斥锁
        pthread_cond_destroy(&cond);
        pthread_mutex_destroy(&mutex);
    
        std::cout << "程序运行结束" << std::endl;
        return EXIT_SUCCESS;
    }
    
    /*
    	运行结果:
    	主线程: 生产者=2, 消费者1=3, 消费者2=4, 消费者3=5
    	生产者(2): 生产了3辆小米su7
    	消费者(5): 消费了一辆小米su7,很开心
    	消费者(3): 消费了一辆小米su7,很开心
    	消费者(4): 消费了一辆小米su7,很开心
    	程序运行结束
    */
    
信号量(Semaphore)
  • 基本概念

    • 同步机制:确保多个线程按特定顺序执行,避免临界资源抢占
    • 生产者-消费者模型:消费者线程必须等待生产者线程先执行
    • 操作原理
      • 线程执行前需申请信号量资源(P操作)
      • 若value>0,申请成功,value减1
      • 若value=0,线程阻塞等待
      • 其他线程释放资源(V操作)使value加1
  • 基本用法:

    #include 
    //定义信号量
    sem_t sem;  // 定义无名信号量变量
    
    //初始化信号量
    int sem_init(sem_t *sem, int pshared, unsigned int value);
    /*
    	初始化无名信号量,最主要是初始化value值
    	sem:信号量地址
    	pshared:0表示线程间同步,非0表示进程间同步
    	value:信号量初始值
    	返回值:成功返回0,失败返回-1
    */
    
    //申请资源
    int sem_wait(sem_t *sem);
    /*
    	功能:阻塞申请无名信号量中的资源,成功申请后,会将无名信号量的value进行减1操作,如果当前无名信号量的value为0,则阻塞 
    	参数:无名信号量的地址 
    	返回值:成功返回0,失败返回-1并置位错误码
    */
    
    //释放资源
    int sem_post(sem_t *sem);
    /*
    	功能:将无名信号量的value值增加1操作
        参数:无名信号量地址 
        返回值:成功返回0,失败返回-1并置位错误码
    */
    
    //销毁资源
    int sem_destroy(sem_t *sem);
    /*
    	功能:销毁无名信号量
        参数:无名信号量地址
        返回值:成功返回0,失败返回-1并置位错误码
    */
    
  • 特点:

    • 可以设置初始资源数量
    • 适合控制并发访问数量
    • 可用于进程间同步
  • 示例

    #include 
    #include 
    #include 
    #include 
    
    // 定义无名信号量
    sem_t sem;
    
    // 生产者线程
    void* producer(void* arg) {
        int num = 5;
        while (num--) {
            sleep(1);
            printf("生产者:生产了一辆小米su7\n");
            
            // 释放信号量资源(V操作)
            sem_post(&sem);
        }
        pthread_exit(nullptr);
    }
    
    // 消费者线程
    void* consumer(void* arg) {
        int num = 5;
        while (num--) {
            // 申请信号量资源(P操作)
            sem_wait(&sem);
            printf("消费者:消费了一辆小米su7,很开心\n");
        }
        pthread_exit(nullptr);
    }
    
    int main() {
        // 初始化无名信号量
        // 参数1: 信号量指针
        // 参数2: 0表示线程间共享
        // 参数3: 初始值0表示初始无资源
        sem_init(&sem, 0, 0);
    
        // 创建生产者消费者线程
        pthread_t tid_producer, tid_consumer;
        
        if (pthread_create(&tid_producer, nullptr, producer, nullptr) != 0) {
            std::cerr << "生产者线程创建失败" << std::endl;
            return EXIT_FAILURE;
        }
        
        if (pthread_create(&tid_consumer, nullptr, consumer, nullptr) != 0) {
            std::cerr << "消费者线程创建失败" << std::endl;
            return EXIT_FAILURE;
        }
    
        // 打印线程ID(使用标准格式)
        printf("主线程:生产者tid = 0x%lx, 消费者tid = 0x%lx\n", 
              (unsigned long)tid_producer, 
              (unsigned long)tid_consumer);
    
        // 等待线程结束
        pthread_join(tid_producer, nullptr);
        pthread_join(tid_consumer, nullptr);
    
        // 销毁无名信号量
        sem_destroy(&sem);
    
        std::cout << "程序运行结束" << std::endl;
        return EXIT_SUCCESS;
    }
    
    /*
    	代码运行结果:
    	主线程:生产者tid = 0x2, 消费者tid = 0x3
    	生产者:生产了一辆小米su7
    	消费者:消费了一辆小米su7,很开心
    	生产者:生产了一辆小米su7
    	消费者:消费了一辆小米su7,很开心
    	生产者:生产了一辆小米su7
    	消费者:消费了一辆小米su7,很开心
    	生产者:生产了一辆小米su7
    	消费者:消费了一辆小米su7,很开心
    	生产者:生产了一辆小米su7
    	消费者:消费了一辆小米su7,很开心
    	程序运行结束
    */
    
  • 案例

    //练习:使用无名信号量完成,定义三个任务,任务1打印A,任务2打印B,任务3打印C,最终输出的结果为ABCABCABCABC...
    #include 
    #include 
    #include 
    #include 
    
    // 定义三个无名信号量
    sem_t sem1, sem2, sem3;
    
    // 打印A的线程
    void* taskA(void* arg) {
        while (true) {
            sem_wait(&sem1);  // 等待sem1
            sleep(1);
            std::cout << "A" << std::flush;
            sem_post(&sem2);  // 释放sem2
        }
        return nullptr;
    }
    
    // 打印B的线程
    void* taskB(void* arg) {
        while (true) {
            sem_wait(&sem2);  // 等待sem2
            sleep(1);
            std::cout << "B" << std::flush;
            sem_post(&sem3);  // 释放sem3
        }
        return nullptr;
    }
    
    // 打印C的线程
    void* taskC(void* arg) {
        while (true) {
            sem_wait(&sem3);  // 等待sem3
            sleep(1);
            std::cout << "C" << std::flush;
            sem_post(&sem1);  // 释放sem1
        }
        return nullptr;
    }
    
    int main() {
        // 初始化信号量
        sem_init(&sem1, 0, 1);  // sem1初始为1,让A先执行
        sem_init(&sem2, 0, 0);
        sem_init(&sem3, 0, 0);
    
        // 创建三个线程
        pthread_t tidA, tidB, tidC;
        
        if (pthread_create(&tidA, nullptr, taskA, nullptr) != 0) {
            std::cerr << "线程A创建失败" << std::endl;
            return EXIT_FAILURE;
        }
        
        if (pthread_create(&tidB, nullptr, taskB, nullptr) != 0) {
            std::cerr << "线程B创建失败" << std::endl;
            return EXIT_FAILURE;
        }
        
        if (pthread_create(&tidC, nullptr, taskC, nullptr) != 0) {
            std::cerr << "线程C创建失败" << std::endl;
            return EXIT_FAILURE;
        }
    
        // 等待线程结束(实际上不会执行到这里)
        pthread_join(tidA, nullptr);
        pthread_join(tidB, nullptr);
        pthread_join(tidC, nullptr);
    
        // 销毁信号量(实际不会执行到这里)
        sem_destroy(&sem1);
        sem_destroy(&sem2);
        sem_destroy(&sem3);
    
        return EXIT_SUCCESS;
    }
    
屏障(Barrier)
  • 概念:屏障(barrier)是一种线程同步机制,它允许多个线程在某个执行点相互等待,直到所有参与的线程都到达该点后才能继续执行。

    1. 核心功能:同步多个线程的执行点
    2. 典型应用场景
      • 并行计算中的阶段同步
      • 多线程初始化完成后的统一启动
      • 迭代算法中每轮迭代的同步
  • 基本用法:

    //初始化屏障
    int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr,unsigned int count);
    /*
    	barrier: 屏障对象指针
    	attr: 屏障属性(通常为NULL)
    	count: 需要等待的线程数量
    	返回值:成功返回0,失败返回错误码
    */
    
    //等待屏障
    int pthread_barrier_wait(pthread_barrier_t *barrier);
    /*
    	线程调用此函数将阻塞,直到足够数量的线程到达屏障
    	最后一个到达的线程会唤醒所有等待线程
    
    	返回值:对最后一个到达的线程返回PTHREAD_BARRIER_SERIAL_THREAD
    			其他线程返回0
    			失败返回错误码
    */
    
    //销毁屏障
    int pthread_barrier_destroy(pthread_barrier_t *barrier);
    
  • 特点

    • 等待指定数量线程到达同步点
    • 常用于并行计算分段处理
  • 示例

    #include 
    #include 
    #include 
    
    #define THREAD_NUM 3
    
    pthread_barrier_t barrier;
    
    void* task(void* arg) {
    	long id = *(long*)arg;
    	
    	std::cout << "线程" << id << "开始执行第一阶段" << std::endl;
    	sleep(id + 1);  // 模拟不同耗时
    	
    	// 等待所有线程到达屏障
    	int ret = pthread_barrier_wait(&barrier);
    	if (ret == PTHREAD_BARRIER_SERIAL_THREAD) {
    		std::cout << "所有线程已到达屏障,最后一个线程是" << id << std::endl;
    	}
    	
    	std::cout << "线程" << id << "开始执行第二阶段" << std::endl;
    	
    	return nullptr;
    }
    
    int main() {
    	pthread_t threads[THREAD_NUM];
    	long threads_id[THREAD_NUM];
    	
    	// 初始化屏障,等待3个线程
    	pthread_barrier_init(&barrier, NULL, THREAD_NUM);
    	
    	// 创建线程
    	for (long i = 0; i < THREAD_NUM; ++i) {
    		threads_id[i] = i;
    		pthread_create(&threads[i], NULL, task, &threads_id[i]);
    	}
    	
    	// 等待线程结束
    	for (int i = 0; i < THREAD_NUM; ++i) {
    		pthread_join(threads[i], NULL);
    	}
    	
    	// 销毁屏障
    	pthread_barrier_destroy(&barrier);
    	
    	return 0;
    }
    
三、同步机制对比
机制 适用场景 优点 缺点
互斥锁 简单临界区保护 简单高效 可能死锁
读写锁 读多写少场景 提高读并发性能 实现复杂
条件变量 线程间状态通知 避免忙等待 必须配合互斥锁使用
信号量 资源数量控制/进程间同步 灵活控制并发量 操作较复杂
屏障 并行计算同步 确保阶段同步 不适用于动态线程数

你可能感兴趣的:(linux)