12.linux笔记:线程
1.什么是线程:
线程指的是进程当中的执行流。
进程:程序放到内存里的可执行实例,进程是程序的一个实例。
同一个程序:若多次读取到内存中,则变成多个独立的进程。
内存有地址,内存中的每个进程内部都有虚拟独立的地址空间。这样,在进程里面,就可以用虚拟地址来访问进程内的地方。
进程是程序执行的完整单位,进程间通信IPC。
一个进程里有好多线程,每个线程里有:程序计数器、寄存器、堆栈、全局变量。
线程是并行的最小单位,cpu为每个线程分配虚拟cpu,不需要时间片轮转,实现真正并行。
2.POSIX标准的线程
POSIX标准就是一套规范,方便移植。
什么是POSIX 线程?
POSIX.1 标准的线程通常称为POSIX 线程或Pthreads
POSIX指定每个线程独有属性:
线程ID、errno变量(perror会打印对应线程的errno的错误信息)
POSIX.1 要求线程共享一系列属性
线程共享相同的全局内存(数据段和堆段)
每个线程都有自己的堆栈(自动变量)
各种标识符:进程ID、父进程 ID、进程组 ID 和会话 ID等
各种资源:控制终端、打开的文件描述符等
进程的属性或设置:信号处置、文件权限掩码(umask)
Pthreads 函数
Linux下的POSIX threads(线程)
已淘汰:原始的linux下没有进程,只有轻量级进程Linux thread
3.线程的创建
主线程
main函数开始的地方是主线程。
每个进程在创建时,都会自动生成一个线程,这就是主线程。它负责执行程序代码(从main函数开始),是这个工厂里的第一条、也是最重要的一条生产线。
主线程里用pthread_create创建的线程叫附加线程,附加线程会受到主线程执行流的影响。主线程结束,所有其他附加线程也会结束。
#include <pthread.h> #include <stdio.h> #include <unistd.h> void *func (void*arg){ printf("helloworld\n"); return NULL; } int main(int argc, const char *argv[]) { pthread_t tid; pthread_create(&tid,NULL,func,NULL); // sleep(1); pthread_join(tid,NULL); return 0; }pthread_join()
相当于进程概念中的wait
pthread_create()函数
1.第一个参数thread:线程号ID,用pthread_t 创建线程ID的内存。pthread_create创建好线程以后,会把线程ID放到pthread_t创建的ID内存里。
2.第二个参数attr:线程的属性,如果传NULL就会采用默认属性
3.第三个参数:是个函数指针,函数名退化成的指针,指向一个返回值和参数都是void*的函数。用来把各种函数数据通过指针传给线程。
4.第四个参数:第三个参数的函数的参数。也就是start_routine()的参数
pthread_create函数如果创建失败,不会直接设置errno,所以如果少了 errno = ret,perror()输出的是errno默认的内容。
pthread_create底层会调用,clone接口。clone能够设置errno。
实例1:资源耗尽,无法继续创建线程
#include <pthread.h> #include <stdio.h> #include <unistd.h> #include <errno.h> void *func (void*arg){ //printf("helloworld\n"); return NULL; } int main(int argc, const char *argv[]) { int ret = 0; pthread_t tid; while(ret == 0){ ret = pthread_create(&tid,NULL,func,NULL); } errno = ret; perror("pthread_create"); // sleep(1); // pthread_join(tid,NULL); return 0; }实例2:创建失败的错误处理
#include <pthread.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <stdlib.h> void *func (void*arg){ printf("helloworld\n"); return NULL; } int main(int argc, const char *argv[]) { int ret = 0; pthread_t tid; ret = pthread_create(&tid,NULL,func,NULL); if(ret != 0){ errno = ret; perror("pthread_create"); exit(EXIT_FAILURE); } // sleep(1); pthread_join(tid,NULL); return 0; }#include <pthread.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> void *func (void*arg){ printf("helloworld\n"); return NULL; } int main(int argc, const char *argv[]) { int ret = 0; pthread_t tid; ret = pthread_create(&tid,NULL,func,NULL); //if(ret != 0){ char *str = strerror(ret); printf("%s\n",str); // exit(EXIT_FAILURE); //} // sleep(1); pthread_join(tid,NULL); return 0; }#include <pthread.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> void *func (void*arg){ printf("helloworld\n"); pthread_exit(NULL); //return NULL; } int main(int argc, const char *argv[]) { int ret = 0; pthread_t tid; ret = pthread_create(&tid,NULL,func,NULL); if(ret != 0){ char *str = strerror(ret); fprintf(stderr,"pthread creat:%s\n",str); exit(EXIT_FAILURE); } // sleep(1); ret = pthread_join(tid,NULL); if(ret != 0){ errno = ret; perror("pthread join"); exit(EXIT_FAILURE); } return 0; }pthread_create返回0成功,非0为失败,不是小于0。
pthread_create()不会设置errno这个全局变量
Resource temporaril unavailable 资源咱不可用
Cannot allocate memory无法分配内存
4.线程的终止与分离属性
return、pthread_exit、exit区别
调用
pthread_exit(),指定一个退出状态值从
start_routine()的 return语句 返回( 相当于调用pthread_exit())被取消(
pthread_cancel())进程被终止,将导致所有线程终止
#include <pthread.h> #include <stdio.h> #include <unistd.h> void* child_thread(void* arg) { while(1) { printf("子线程正在运行...\n"); sleep(1); } return NULL; } int main() { pthread_t tid; pthread_create(&tid, NULL, child_thread, NULL); sleep(2); // 情况1: return 0; → 进程终止,子线程被强制结束 // 情况2: pthread_exit(NULL); → 仅主线程结束,子线程继续运行 pthread_exit(NULL); // 子线程会继续运行 // return 0; // 子线程会立即终止 }#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> void *func (void *arg) { printf("Hello thread!\n"); sleep(10); printf("thread done.\n"); //由于线程被主线程取消,所以这里不会被执行 pthread_exit(NULL); } int main(int argc, const char *argv[]) { pthread_t tid; int ret = pthread_create(&tid, NULL, func, NULL); if(ret != 0){ errno = ret; perror("pthread_create"); exit(EXIT_FAILURE); } sleep(1); pthread_cancel(tid); printf("tid=%lu\n", tid); pthread_join(tid, NULL); return 0; }exit不管在哪个线程,都会终止整个进程。pthread_exit不管在主/子线程,都只会终止那一个线程
线程的执行顺序不确定:
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> void *func (void *arg) { printf("id = %d\n",(int)(long)arg); pthread_exit(NULL); } int main(int argc, const char *argv[]) { pthread_t tid[10]; int id[10]; int ret; for(int i = 0; i < 10;i++){ id[i] = i; ret = pthread_create(&tid[i], NULL, func, (void*)(long)id[i]); if(ret != 0){ errno = ret; perror("pthread_create"); exit(EXIT_FAILURE); } } for(int i = 0; i < 10;i++){ ret = pthread_join(tid[i], NULL); if(ret != 0){ errno = ret; perror("pthread_join"); exit(EXIT_FAILURE); } } return 0; }线程的分离属性
线程的执行顺序是不确定的
线程是可连接的,或者是分离的
可连接的调用
pthread_join()等待线程终止并获取退出状态分离的线程其资源会自动放回系统
pthread_detach(pthread_self())
设置线程为分离属性
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> void *func (void *arg) { pthread_detach(pthread_self()); //设置线程为分离属性 printf("detach thread.\n"); pthread_exit(NULL); } int main(int argc, const char *argv[]) { pthread_t tid; int ret = pthread_create(&tid, NULL, func, NULL); if(ret != 0){ errno = ret; perror("pthread_create"); exit(EXIT_FAILURE); } // pthread_detach(tid); //也可以在主线程当中设置创建线程的分离属性 printf("tid=%lu\n", tid); sleep(1); return 0; }pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <signal.h> #include <unistd.h> void *func (void *arg) { printf("detach thread.\n"); pthread_exit(NULL); } int main(int argc, const char *argv[]) { pthread_t tid; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); int ret = pthread_create(&tid, &attr, func, NULL); if(ret != 0){ errno = ret; perror("pthread_create"); exit(EXIT_FAILURE); } pthread_attr_destroy(&attr); printf("tid=%lu\n", tid); sleep(1); return 0; }pthread“不透明”的数据类型
不透明的数据类型,属性设置时,用特定函数接口来实现
pthread_join连接函数
连接正常终止的线程:
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> void *func (void *arg) { pthread_exit("thread exit"); } int main(int argc, const char *argv[]) { pthread_t tid; int ret = pthread_create(&tid, NULL, func, NULL); if(ret != 0){ errno = ret; perror("pthread_create"); exit(EXIT_FAILURE); } void *retval; ret = pthread_join(tid, &retval); //func返回的字符串的地址,被放到retval里 if(ret != 0){ errno = ret; perror("pthread_create"); exit(EXIT_FAILURE); } printf("retval:%s\n", (char *)retval); retval是void*指针,内容是func返回的字符串的地址 return 0; }#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> static int a = 10; void *func (void *arg) { int *p = &a; pthread_exit(p); } int main(int argc, const char *argv[]) { pthread_t tid; int ret = pthread_create(&tid, NULL, func, NULL); if(ret != 0){ errno = ret; perror("pthread_create"); exit(EXIT_FAILURE); } void *retval; ret = pthread_join(tid, &retval); if(ret != 0){ errno = ret; perror("pthread_create"); exit(EXIT_FAILURE); } printf("retval:%d\n", *(int *)retval); return 0; }这里解释下:pthread_join的第二个参数,pthread_join是把线程的返回值,给二级指针指向的内存,也就是一级指针。!!!!!!把线程返回值赋给,一级指针。上面的例子:retval = p;
连接被取消的线程:
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> static int a = 10; void *func (void *arg) { sleep(100); int *p = &a; pthread_exit(p); } int main(int argc, const char *argv[]) { pthread_t tid; int ret = pthread_create(&tid, NULL, func, NULL); if(ret != 0){ errno = ret; perror("pthread_create"); exit(EXIT_FAILURE); } sleep(1); pthread cancel(tid); void *retval; ret = pthread_join(tid, &retval); if(ret != 0){ errno = ret; perror("pthread_create"); exit(EXIT_FAILURE); } if(retval == PTHREAD_CANCELED){ printf("retval:%d\n",(int)retval); } return 0; }多线程实验,一个耗时的算法:
单线程:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <time.h> int main(int argc, const char *argv[]) { unsigned long long a; if(argc != 2) { fprintf(stderr, "Usage %s [loops]\n", argv[0]); exit(EXIT_FAILURE); } if(sscanf(argv[1], "%llu", &a) != 1) { fprintf(stderr, "loops Invalid.\n"); exit(EXIT_FAILURE); } time_t t; time(&t); for(unsigned long long i = 0; i < a; i++); time_t t2; time(&t2); printf("进程使用了 %ld 秒\n", t2 - t); return 0; }多线程:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <time.h> #include <pthread.h> #include <string.h> void *ThreadFunc(void *arg) { unsigned long long a = *(unsigned long long *)arg; for(unsigned long long i = 0; i < a; i++); pthread_exit(NULL); } int main(int argc, const char *argv[]) { unsigned long long a; if(argc != 2) { fprintf(stderr, "Usage %s [loops]\n", argv[0]); exit(EXIT_FAILURE); } if(sscanf(argv[1], "%llu", &a) != 1) { fprintf(stderr, "loops Invalid.\n"); exit(EXIT_FAILURE); } time_t t; time(&t); unsigned long long loops = a / 4; pthread_t tid[4] = {}; for(int i = 0; i < 4; i++) { int ret = pthread_create(&tid[i], NULL, ThreadFunc, &loops); if(ret != 0) { fprintf(stderr, "pthread_create:%s\n", strerror(ret) ); exit(EXIT_FAILURE); } } for(int i = 0; i < 4; i++) { int ret = pthread_join(tid[i], NULL); if(ret != 0) { fprintf(stderr, "pthread_join:%s\n", strerror(ret) ); exit(EXIT_FAILURE); } } time_t t2; time(&t2); printf("进程使用了 %ld 秒\n", t2 - t); return 0; }5.线程同步
互斥量解决同步问题
/* 为突出问题 代码省略了错误处理*/ #include <pthread.h> #include <stdio.h> #include <stdlib.h> static int glob; // 全局变量,被所有线程共享 // 线程函数 void *ThreadFunc(void *arg) { int loops = *((int *)arg); // 从参数中获取循环次数 int loc, j; for(j = 0; j < loops; j++) { loc = glob; // 读取全局变量 或者把这三行改成g++;原子操作 loc++; // 局部变量自增 glob = loc; // 将局部变量的值赋给全局变量 } pthread_exit(NULL); // 线程退出 } int main(int argc, const char *argv[]) { pthread_t tid1, tid2; // 定义两个线程 ID int loops; // 检查命令行参数 if(argc != 2) { fprintf(stderr, "%s [loops]\n", argv[0]); exit(EXIT_FAILURE); } // 从命令行参数中读取循环次数 if( sscanf(argv[1], "%d", &loops) != 1) { fprintf(stderr, "Invalid loops\n"); exit(EXIT_FAILURE); } // 创建两个线程,都执行 ThreadFunc 函数 pthread_create(&tid1, NULL, ThreadFunc, &loops); pthread_create(&tid2, NULL, ThreadFunc, &loops); // 等待两个线程结束 pthread_join(tid1, NULL); pthread_join(tid2, NULL); // 打印最终的全局变量值 printf("glob = %d\n", glob); return 0; }互斥量:同一时刻只能让一个线程执行这行代码
静态分配的互斥量
如果发起pthread_mutex_lock()调用的线程自身之前已然将目标互斥量锁定,对于互斥量的默认类型而言,可能会产生两种后果—视具体实现而定:线程陷入死锁(deadlock),因试图锁定已为自己所持有的互斥量而遭到阻塞;或者调用失败,返回EDEADLK错误。在Linux上,默认情况下线程会发生死锁。
函数pthread_mutex_unlock()将解锁之前已遭调用线程锁定的互斥量。以下行为均属错误:
对处于未锁定状态的互斥量进行解锁
解锁由其他线程锁定的互斥量
如果有不止一个线程在等待获取由函数pthread_mutex_unlock()解锁的互斥量,则无法判断究竟哪个线程将如愿以偿。
临界区
加锁和解锁中间的代码叫临界区。临界区访问的资源叫临界资源,同一时刻只能由一个线程访问临界资源。
销毁互斥量:int pthread_mutex_destroy(&mutex)函数
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> static int glob; //临界资源 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 静态初始化互斥量 void *ThreadFunc(void *arg) { int loops = *((int *)arg); int loc, j; for(j = 0; j < loops; j++) { pthread_mutex_lock(&mutex); //加锁与解锁的代码区域被称为 临界区 loc = glob; loc++; glob = loc; pthread_mutex_unlock(&mutex); } pthread_exit(NULL); } int main(int argc, const char *argv[]) { pthread_t tid1, tid2; int loops; if(argc != 2) { fprintf(stderr, "%s [loops]\n", argv[0]); exit(EXIT_FAILURE); } if( sscanf(argv[1], "%d", &loops) != 1) { fprintf(stderr, "Invalid loops\n"); exit(EXIT_FAILURE); } int ret = pthread_create(&tid1, NULL, ThreadFunc, &loops); if(ret != 0) { fprintf(stderr, "pthread_create:%s\n", strerror(ret)); exit(EXIT_FAILURE); } ret = pthread_create(&tid2, NULL, ThreadFunc, &loops); if(ret != 0) { fprintf(stderr, "pthread_create:%s\n", strerror(ret)); exit(EXIT_FAILURE); } ret = pthread_join(tid1, NULL); if(ret != 0) { fprintf(stderr, "pthread_join tid1:%s\n", strerror(ret)); exit(EXIT_FAILURE); } ret = pthread_join(tid2, NULL); if(ret != 0) { fprintf(stderr, "pthread_join tid2:%s\n", strerror(ret)); exit(EXIT_FAILURE); } printf("glob = %d\n", glob); return 0; }使用锁的时候,先加锁,再解锁,不要有其他操作。
能使用一把锁,就只使用一把锁。如果一定要使用两把锁,不要嵌套使用。
动态初始化的互斥量
/* 为突出问题 代码省略了错误处理*/ #include <pthread.h> #include <stdio.h> #include <stdlib.h> static int glob; struct thread { int loops; pthread_mutex_t mutex; }; // 线程函数 void *ThreadFunc(void *arg) { struct thread *data = (struct thread *)arg; // 从参数中获取循环次数 int loc, j; for(j = 0; j <>/* 为突出问题 代码省略了错误处理 */ #include <pthread.h> #include <stdio.h> #include <stdlib.h> static int glob; int loops; // ✅ 修正:英文分号 pthread_mutex_t mutex; // 注意:这里是普通变量,不是指针 // 线程函数 void *ThreadFunc(void *arg) { int loc, j; int n = *((int *)arg); // ✅ 改名避免冲突,或用全局 loops for(j = 0; j < n; j++) { // 用局部变量 n 控制循环 pthread_mutex_lock(&mutex); glob++; pthread_mutex_unlock(&mutex); } pthread_exit(NULL); } int main(int argc, const char *argv[]) { pthread_t tid1, tid2; pthread_mutex_init(&mutex, NULL); // ✅ 修正:传地址 &mutex if(argc != 2) { fprintf(stderr, "%s [loops]\n", argv[0]); exit(EXIT_FAILURE); } if(sscanf(argv[1], "%d", &loops) != 1) { // ✅ 修正:传 &loops fprintf(stderr, "Invalid loops\n"); exit(EXIT_FAILURE); } pthread_create(&tid1, NULL, ThreadFunc, &loops); pthread_create(&tid2, NULL, ThreadFunc, &loops); pthread_join(tid1, NULL); pthread_join(tid2, NULL); pthread_mutex_destroy(&mutex); // ✅ 修正:传地址 printf("glob = %d\n", glob); return 0; }思考:如何解决以下代码的数据同步问题?
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #include <time.h> #define NUM_READERS 5 #define NUM_WRITERS 2 int balance = 1000; // 初始账户余额 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 读取者线程函数 void* reader(void* arg) { int id = *((int*)arg); while (1) { pthread_mutex_lock(&mutex); printf("读者 %d: 当前余额为 %d 元\n", id, balance); sleep(2); pthread_mutex_unlock(&mutex); sleep(rand() % 3); // 随机休眠 } return NULL; } // 写入者线程函数 void* writer(void* arg) { int id = *((int*)arg); while (1) { pthread_mutex_lock(&mutex); int amount = rand() % 100; // 随机生成一个金额 balance += amount; // 修改余额 sleep(2); printf("写者 %d: 存入 %d 元,最新余额为 %d 元\n", id, amount, balance); pthread_mutex_unlock(&mutex); sleep(rand() % 3); // 随机休眠 } return NULL; } int main() { pthread_t readers[NUM_READERS]; //5个元素 pthread_t writers[NUM_WRITERS]; //2个元素 int reader_ids[NUM_READERS]; //5个元素 int writer_ids[NUM_WRITERS]; //2个元素 srand(time(NULL)); for (int i = 0; i < NUM_READERS; i++) { reader_ids[i] = i + 1; pthread_create(&readers[i], NULL, reader, &reader_ids[i]); } for (int i = 0; i < NUM_WRITERS; i++) { writer_ids[i] = i + 1; pthread_create(&writers[i], NULL, writer, &writer_ids[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); } return 0; }如果不加锁:
读写锁
多线程并发的程序,不是立刻就能发现数据的同步问题,往往运行一段时间才会出现出错,原因是数据没有做好同步工作,没有做好互斥量。(互斥锁)
写锁独占时,读锁也不能访问。
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #include <time.h> #define NUM_READERS 5 #define NUM_WRITERS 2 int balance = 1000; // 初始账户余额 pthread_rwlock_t rwlock; // 读写锁 // 读取者线程函数 void* reader(void* arg) { int id = *((int*)arg); while (1) { pthread_rwlock_rdlock(&rwlock); // 获取读锁 printf("读者 %d: 当前余额为 %d 元\n", id, balance); pthread_rwlock_unlock(&rwlock); // 释放读锁 sleep(rand() % 3); // 随机休眠 } return NULL; } // 写入者线程函数 void* writer(void* arg) { int id = *((int*)arg); while (1) { int amount = rand() % 100; // 随机生成一个金额 pthread_rwlock_wrlock(&rwlock); // 获取写锁 balance += amount; // 修改余额 printf("写者 %d: 存入 %d 元,最新余额为 %d 元\n", id, amount, balance); pthread_rwlock_unlock(&rwlock); // 释放写锁 sleep(rand() % 3); // 随机休眠 } return NULL; } int main() { pthread_t readers[NUM_READERS]; pthread_t writers[NUM_WRITERS]; int reader_ids[NUM_READERS]; int writer_ids[NUM_WRITERS]; srand(time(NULL)); // 初始化随机数种子 // 初始化读写锁 pthread_rwlock_init(&rwlock, NULL); // 创建读取者线程 for (int i = 0; i < NUM_READERS; i++) { reader_ids[i] = i + 1; pthread_create(&readers[i], NULL, reader, &reader_ids[i]); } // 创建写入者线程 for (int i = 0; i < NUM_WRITERS; i++) { writer_ids[i] = i + 1; pthread_create(&writers[i], NULL, writer, &writer_ids[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; }6.条件变量
解决生产者与消费者问题通常使用以下几种同步机制:
信号量(Semaphore):
2.条件变量(Condition Variable):
条件变量通常与互斥锁(Mutex)结合使用,用于线程之间的同步。条件变量允许线程在某个条件不满足时等待,而当条件满足时被唤醒。伪代码示例如下:
#include <pthread.h> #include <semaphore.h> #define BUFFER_SIZE 10 int buffer[BUFFER_SIZE]; int count = 0; int in = 0, out = 0; pthread_mutex_t mutex; pthread_cond_t not_full, not_empty; void* producer(void* arg) { while (true) { int item = produce_item(); // 生成一个项目 pthread_mutex_lock(&mutex); while (count == BUFFER_SIZE) { pthread_cond_wait(¬_full, &mutex); // 缓冲区满时等待 } buffer[in] = item; // 将项目放入缓冲区 in = (in + 1) % BUFFER_SIZE; count++; pthread_cond_signal(¬_empty); // 通知消费者有新项目 pthread_mutex_unlock(&mutex); } return NULL; } void* consumer(void* arg) { while (true) { pthread_mutex_lock(&mutex); while (count == 0) { pthread_cond_wait(¬_empty, &mutex); // 缓冲区空时等待 } int item = buffer[out]; // 从缓冲区取出项目 out = (out + 1) % BUFFER_SIZE; count--; pthread_cond_signal(¬_full); // 通知生产者缓冲区有空位 pthread_mutex_unlock(&mutex); consume_item(item); // 处理项目 } return NULL; }3.队列: 使用一个线程安全的队列来管理缓冲区,生产者将数据放入队列,消费者从队列中取出数据。队列通常内部已经实现了同步机制,使用起来更加方便。伪代码示例如下:
import threading import queue BUFFER_SIZE = 10 buffer_queue = queue.Queue(BUFFER_SIZE) def producer(): while True: item = produce_item() # 生成一个项目 buffer_queue.put(item) # 将项目放入队列 print(f"Produced: {item}") def consumer(): while True: item = buffer_queue.get() # 从队列取出项目 consume_item(item) # 处理项目 buffer_queue.task_done() print(f"Consumed: {item}") # 创建生产者和消费者线程 producer_thread = threading.Thread(target=producer) consumer_thread = threading.Thread(target=consumer) # 启动线程 producer_thread.start() consumer_thread.start() # 等待所有线程完成 producer_thread.join() consumer_thread.join()应用场景
生产者与消费者问题在多线程编程中广泛应用于各种场景,例如:
数据处理:生产者线程生成数据,消费者线程处理数据。
任务调度:生产者线程生成任务,消费者线程执行任务。
消息传递:生产者线程发送消息,消费者线程接收并处理消息。
注意事项
死锁:确保线程不会因为等待条件变量而陷入死锁。通常使用
while循环来检查条件,而不是if语句。性能:避免频繁的线程切换,可以通过批量处理数据或使用更高效的同步机制来提高性能。
公平性:确保生产者和消费者线程之间有合理的调度,避免某个线程长时间占用资源。
通过以上方法,可以有效地解决生产者与消费者问题,确保多线程程序的正确性和高效性。
什么是条件变量?
条件变量(''condition variable'' 的缩写)是一种同步设备
条件变量允许一个线程就某个共享变量(或其他共享资源)的状态变化通知其他线程,并让其他线程等待(堵塞于)这一通知。
条件变量是怎样工作的?
条件变量的使用
条件变量是在加锁的基础上,再操作的。
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #include <time.h> //初始化条件变量 pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //初始化互斥量 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; #define NUM_CONSUMER 5 //消费者线程数量 #define NUM_PRODUCER 2 //生产者线程数量 int balance; // 初始账户余额为0 // 消费者线程函数 void *consumer(void* arg) { int id = *((int*)arg); while (1) { int amount = rand() % 100; // 随机生成一个金额 pthread_mutex_lock(&mutex); do { pthread_cond_wait(&cond, &mutex); //解锁,阻塞等待, 收到信号以后再加锁,访问临界资源 } while(balance < amount); balance -= amount; // 消费 printf("[消费者ID:%d]消费 %d 元: 当前余额为 %d 元\n", id, amount, balance); pthread_mutex_unlock(&mutex); sleep(rand() % 3); // 随机休眠 } return NULL; } // 生产者线程 void* producer(void* arg) { int id = *((int*)arg); while (1) { int amount = rand() % 100; // 随机生成一个金额 pthread_mutex_lock(&mutex); balance += amount; // 增加余额 printf("[生产者ID:%d] 存入 %d 元,余额为 %d 元\n", id, amount, balance); pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); sleep(rand() % 3); // 随机休眠 } return NULL; } int main(int argc, const char *argv[]) { pthread_t ctid[NUM_CONSUMER]; pthread_t ptid[NUM_PRODUCER]; int cids[NUM_CONSUMER]; int pids[NUM_PRODUCER]; srand(time(NULL)); // 初始化随机数种子 for (int i = 0; i < NUM_CONSUMER; i++) { cids[i] = i + 1; pthread_create(&ctid[i], NULL, consumer, &cids[i]); } for (int i = 0; i < NUM_PRODUCER; i++) { pids[i] = i + 1; pthread_create(&ptid[i], NULL, producer, &pids[i]); } for (int i = 0; i < NUM_CONSUMER; i++) { pthread_join(ctid[i], NULL); } for (int i = 0; i < NUM_PRODUCER; i++) { pthread_join(ptid[i], NULL); } pthread_cond_destroy(&cond); pthread_mutex_destroy(&mutex); return 0; }条件变量的虚假唤醒
只要用了条件变量,做好while循环,来判断
