多线程基础
1> 多线程的引入:也是为了实现多任务并发执行的问题的,能够实现多个阻塞任务同时执行
2> 多线程(LWP轻量版进程):线程是粒度更小的任务执行单元
3> 进程是资源分配的基本单位,而线程是任务器进行任务调度的最小单位
4> 一个进程可以拥有多个线程,同一个进程中的多个线程共享进程的资源
5> 由于线程是共用进程的资源,所以对于线程的切换而言,开销较小
6> 由于多个线程使用的是同一个进程的资源,那么,就会导致每个进程使用资源时,产生资源抢占问题,没有多进程安全
7> 每个进程至少有一个线程:主线程
8> 只要有一个线程中退出了进程,那么所有的线程也就结束了
主线程结束后,整个进程也就结束了
9> 多个线程执行顺序:没有先后顺序,按时间片轮询,上下文切换,抢占CPU的方式进行
多线程编程
由于C库没有提供有关多线程的相关操作,对于多线程编程要依赖于第三方库
头文件:#include<pthread.h>
编译时:需要加上 -lpthread 选项,链接上对于的线程支持库
创建线程:pthread_create
1 2 3 4 5 6 7 8 9 10 11
| #include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void * (*start_routine) (void *), void *arg); 功能:创建一个分支线程 参数1:线程号,通过参数返回,用法:在外部定义一个该类型的变量,将地址传递入函数,调用结束后,该变量中即是线程号 参数2:线程属性,一般填NULL,让系统使用默认属性创建一个线程 参数3:是一个回调函数,一个函数指针,需要向该参数中传递一个函数名,作为线程体执行函数 该函数由用户自己定义,参数是void*类型,返回值也是void *类型 参数4: 是参数3的参数,如果不想向线程体内传递数据,填NULL即可 返回值:成功返回0,失败返回一个错误码(非linux内核的错误码,是线程支持库中定义的一个错误码,也就是这里perror没有置位用不了) Compile and link with -pthread.
|
1> 不向线程体传递数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include<myhead.h>
void *task(void *arg) { while(1) { printf("我是分支线程\n"); sleep(1); } }
int main(int argc, const char *argv[]) { pthread_t tid = -1; if(pthread_create(&tid, NULL, task, NULL) != 0) { printf("tid create error\n"); return -1; } printf("pthread_create success\n"); while(1) { printf("我是主线程\n"); sleep(1); } while(1); return 0; }
|
2> 向线程体中传递单个数据
用指针传入,传入后需强制转换类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include<myhead.h>
void *task(void *arg) { int key = *(int*)arg; printf("我是分支线程:num = %d\n", key); }
int main(int argc, const char *argv[]) { pthread_t tid = -1; int num = 520; if(pthread_create(&tid, NULL, task, &num) != 0) { printf("tid create error\n"); return -1; } printf("pthread_create success,tid = %#lx\n", tid); printf("我是主线程\n"); num = 1314; printf("主线程中num = %d\n", num); while(1); return 0; }
|
3> 向线程体传入多个数据
定义信息结构体传输,传入后需强转类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #include<myhead.h>
struct Info { int num; char name[20]; double score; };
void *task(void *arg) { Info buf = *((Info*)arg); printf("分支线程中:num = %d, name = %s, score = %.2lf\n", buf.num, buf.name, buf.score); }
int main(int argc, const char *argv[]) { pthread_t tid = -1; int num = 520; char name[20] = "zhangsan"; double score = 99.5; Info buf = {num, "zhangsan", score}; if(pthread_create(&tid, NULL, task, &buf) != 0) { printf("tid create error\n"); return -1; } printf("pthread_create success,tid = %#lx\n", tid); printf("我是主线程\n"); while(1); return 0; }
|
线程号的获取:pthread_self
1 2 3 4 5
| #include <pthread.h> pthread_t pthread_self(void); 功能:获取当前线程的线程号 参数:无 返回值:返回调用线程的id号,不会失败
|
线程的退出函数:pthread_exit
1 2 3 4 5
| #include <pthread.h> void pthread_exit(void *retval); 功能:退出当前线程 参数:表示退出时的状态,一般填NULL 返回值:无
|
线程的资源回收:pthread_join
1 2 3 4 5 6
| #include <pthread.h> int pthread_join(pthread_t thread, void **retval); 功能:阻塞回收指定线程的资源,线程无法用join非阻塞回收,但是可以考虑分离态 参数1:要回收的线程号 参数2:线程退出时的状态,一般填NULL 返回值:成功返回0,失败返回一个错误码
|
线程分离态:pthread_detach
1 2 3 4 5
| #include <pthread.h> int pthread_detach(pthread_t thread); 功能:将指定线程设置成分离态,被设置成分离态的线程,退出后,资源由系统自动回收 参数:要分离的线程号 返回值:成功返回0,失败返回一个错误码
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include <myhead.h>
void *task(void *arg) { printf("分支线程,tid = %#x\n", pthread_self()); sleep(3); pthread_exit(NULL); }
int main() { pthread_t tid = -1; if(pthread_create(&tid, NULL, task, NULL) != 0) { printf("pthread_create error\n"); return -1; } printf("主线程,tid = %#x\n", tid); pthread_detach(tid); sleep(5); std::cout << "Hello, World!" << std::endl; return 0; }
|
练习:使用多线程完成两个文件的拷贝,线程1拷贝前一半内容,线程2拷贝后一半内容,主线程用于回收两个分支线程的资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
| #include <myhead.h>
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 += 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> 由于同一个进程的多个线程会共享进程的资源,这些被共享的资源称为临界资源
2> 多个线程对公共资源的抢占问题,访问临界资源的代码段称为临界区,临界区里尽量不要放sleep()函数
3> 多个线程抢占进程资源的现象称为竞态
4> 为了解决竞态,我们引入了同步互斥机制
| 工具 |
核心作用 |
一句话理解 |
适用场景 |
互斥锁 mutex |
保护共享资源不被同时修改 |
一把锁,同一时间只能一个人进 |
保护变量、结构体、共享内存 |
无名信号量 sem |
控制 “允许多少人同时进” |
计数器,可以允许多个人进 |
限流、生产者 - 消费者、顺序控制 |
条件变量 cond |
等待某个条件满足再执行 |
等通知,条件不满足就睡觉 |
等待数据、等待队列非空、等待任务完成 |
线程的互斥机制之互斥锁
互斥锁的本质也是一个特殊的临界资源,当该临界资源被某个线程所拥有后,其他线程就不能拥有该资源,直到拥有该资源的线程释放掉互斥锁后,其他线程才能进行抢占(即同一时刻,一个互斥锁只能被一个线程所拥有)
互斥锁API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 1、创建一个互斥锁:只需定义一个pthread_mutex_t 类型的变量即创建了一个互斥锁 pthread_mutex_t mutex;
2、初始化互斥锁 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,失败返回错误码 3、获取锁资源 int pthread_mutex_lock(pthread_mutex_t *mutex); 功能:获取锁资源,如果要获取的互斥锁已经被其他线程锁定,那么该函数会阻塞,直到能够获取锁资源 参数:互斥锁地址,属于地址传递 返回值:成功返回0,失败返回一个错误码 4、释放锁资源 int pthread_mutex_unlock(pthread_mutex_t *mutex); 功能:释放对互斥锁资源的拥有权 参数:互斥锁变量的地址 返回值:成功返回0,失败返回一个错误码 5、销毁互斥锁 int pthread_mutex_destroy(pthread_mutex_t *mutex); 功能:销毁互斥锁 参数:互斥锁变量的地址 返回值:成功返回0,失败返回一个错误码
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| #include <myhead.h>
pthread_mutex_t mutex;
int num = 520;
void *task1(void *arg) { while(1) { sleep(1); pthread_mutex_lock(&mutex); num -= 10; printf("张三取了10,剩余%d\n", num); pthread_mutex_unlock(&mutex); } }
void *task2(void *arg) { while(1) { sleep(1); pthread_mutex_lock(&mutex); num -= 20; printf("李四取了20,剩余%d\n", num); pthread_mutex_unlock(&mutex); } }
int main() { pthread_mutex_init(&mutex, NULL); pthread_t tid1,tid2; if(pthread_create(&tid1, NULL, task1, NULL) != 0) { printf("tid1 create error\n"); return -1; } if(pthread_create(&tid2, NULL, task2, NULL) != 0) { printf("tid2 create error\n"); return -1; } printf("主线程:tid1 = %#x, tid2 = %#x\n", tid1, tid2); pthread_join(tid1, NULL); pthread_join(tid2, NULL); pthread_mutex_destroy(&mutex); std::cout << "Hello, World!" << std::endl; return 0; }
|
线程同步之无名信号量
1> 线程同步:就是多个线程之间有先后顺序得执行,这样在访问临界资源时,就不会产生抢占现象了
2> 同步机制常用于生产者消费者模型:消费者任务要想执行,必须先执行生产者线程,多个任务有顺序执行
3> 无名信号量:本质上也是一个特殊的临界资源,内部维护了一个value值,当某个进行想要执行之前,先申请该无名信号量的value资源,如果value值大于0,则申请资源函数接触阻塞,继续执行后续操作。如果value值为0,则当前申请资源函数会处于阻塞状态,直到其他线程将该value值增加到大于0
无名信号量API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| 1、创建无名信号量:只需定义一个sem_t 类型的变量即可 sem_t sem;
2、初始化无名信号量 #include <semaphore.h> int sem_init(sem_t *sem, int pshared, unsigned int value); 功能:初始化无名信号量,最主要是初始化value值 参数1:无名信号量的地址 参数2:判断进程还是线程的同步 0:表示线程间同步 非0:表示进程间同步,需要创建在共享内存段中 参数3:无名信号量的初始值 返回值:成功返回0,失败返回-1并置位错误码 3、申请无名信号量的资源(P操作(Passeren,通过)) #include <semaphore.h> int sem_wait(sem_t *sem); 功能:阻塞申请无名信号量中的资源,成功申请后,会将无名信号量的value进行减1操作,如果当前无名信号量的value为0,则阻塞 参数:无名信号量的地址 返回值:成功返回0,失败返回-1并置位错误码 4、释放无名信号量的资源(V操作(Verhogen,升高)) #include <semaphore.h> int sem_post(sem_t *sem); 功能:将无名信号量的value值增加1操作 参数:无名信号量地址 返回值:成功返回0,失败返回-1并置位错误码 5、销毁无名信号量 #include <semaphore.h> int sem_destroy(sem_t *sem); 功能:销毁无名信号量 参数:无名信号量地址 返回值:成功返回0,失败返回-1并置位错误码
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| #include <myhead.h>
sem_t sem;
void *task1(void *arg) { int num = 5; while(num--) { sleep(1); printf("我生产了一辆特斯拉\n"); sem_post(&sem); } pthread_exit(NULL); }
void *task2(void *arg) { int num = 5; while(num--) { sem_wait(&sem); printf("我消费了一辆特斯拉,很开心\n"); } pthread_exit(NULL); }
int main() { sem_init(&sem, 0, 0); pthread_t tid1,tid2; if(pthread_create(&tid1, NULL, task1, NULL) != 0) { printf("tid1 create error\n"); return -1; } if(pthread_create(&tid2, NULL, task2, NULL) != 0) { printf("tid2 create error\n"); return -1; } printf("主线程:tid1 = %#x, tid2 = %#x\n", tid1, tid2); pthread_join(tid1, NULL); pthread_join(tid2, NULL); sem_destroy(&sem); std::cout << "Hello, World!" << std::endl; return 0; }
|
练习:使用无名信号量完成,定义三个任务,任务1打印A,任务2打印B,任务3打印C,最终输出的结果为ABCABCABCABC…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| #include <myhead.h>
sem_t sem1, sem2, sem3;
void *task1(void *arg) { while(1) { sem_wait(&sem1); sleep(1); printf("A"); fflush(stdout); sem_post(&sem2); } } void *task2(void *arg) { while(1) { sem_wait(&sem2); sleep(1); printf("B"); fflush(stdout); sem_post(&sem3); } } void *task3(void *arg) { while(1) { sem_wait(&sem3); sleep(1); printf("C"); fflush(stdout); sem_post(&sem1); } }
int main(int argc, const char *argv[]) { sem_init(&sem1, 0, 1); sem_init(&sem2, 0, 0); sem_init(&sem3, 0, 0); pthread_t tid1, tid2, tid3; if(pthread_create(&tid1, NULL, task1, NULL) != 0) { printf("tid1 create error\n"); return -1; } if(pthread_create(&tid2, NULL, task2, NULL) != 0) { printf("tid2 create error\n"); return -1; } if(pthread_create(&tid3, NULL, task3, NULL) != 0) { printf("tid3 create error\n"); return -1; } pthread_join(tid1, NULL); pthread_join(tid2, NULL); pthread_join(tid3, NULL); std::cout << "Hello, World!" << std::endl; return 0; }
|
线程同步之条件变量Condition
1> 条件变量本质上也是一个临界资源,他维护了一个队列,当消费者线程想要执行时,先进入队列中等待生产者的唤醒。执行完生产者,再由生产者唤醒在队列中的消费者,这样就完成了生产者和消费者之间的同步关系。
2> 但是,多个消费者在进入休眠队列的过程是互斥的,所以,在消费者准备进入休眠队列时,需要使用互斥锁来进行互斥操作
注意:同时期只有一个线程能拿到锁,但是当一个线程拿到锁后进行pthread_cond_wait则锁自动unlock了
1 2 3 4 5 6 7 8 9 10 11 12
| pthread_mutex_lock(&mutex);
while(队列为空) { pthread_cond_wait(&cond, &mutex); } pthread_mutex_unlock(&mutex);
|
条件变量API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| 1、创建一个条件变量,只需定义一个pthread_cond_t类型的全局变量即可 pthread_cond_t cond;
2、初始化条件变量 pthread_cond_t cond = PTHREAD_COND_INITIALIZER; int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr); 功能:初始化条件变量 参数1:条件变量的起始地址 参数2:条件变量的属性,一般填NULL 返回值:成功返回0,失败返回一个错误码 3、消费者线程进入等待队列 int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex); 功能:将线程放入休眠等待队列,等待其他线程的唤醒 参数1:条件变量的地址 参数2:互斥锁,由于多个消费者线程进入等待队列时会产生竞态,为了解决竞态,需要使用一个互斥锁 返回值:成功返回0,失败返回错误码 4、生产者线程唤醒休眠队列中的任务 int pthread_cond_broadcast(pthread_cond_t *cond); 功能:唤醒条件变量维护的队列中的所有消费者线程 参数:条件变量的地址 返回值:成功返回0,失败返回错误码 int pthread_cond_signal(pthread_cond_t *cond); 功能:唤醒条件变量维护的队列中的第一个进入队列的消费者线程 参数:条件变量的地址 返回值:成功返回0,失败返回错误码 5、销毁条件变量 int pthread_cond_destroy(pthread_cond_t *cond); 功能:销毁一个条件变量 参数:条件变量的地址 返回值:成功返回0,失败返回错误码
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
| #include <myhead.h>
pthread_cond_t cond;
pthread_mutex_t mutex;
void *task1(void *arg) {
sleep(3); printf("我生产了3辆特斯拉\n"); pthread_cond_broadcast(&cond); pthread_exit(NULL); }
void *task2(void *arg) { pthread_mutex_lock(&mutex); pthread_cond_wait(&cond, &mutex); printf("%#x:消费了一辆特斯拉,很开心\n", pthread_self()); pthread_mutex_unlock(&mutex); pthread_exit(NULL); } int main() { pthread_cond_init(&cond, NULL); pthread_mutex_init(&mutex, NULL); pthread_t tid1,tid2,tid3,tid4; if(pthread_create(&tid1, NULL, task1, NULL) != 0) { printf("tid1 create error\n"); return -1; } if(pthread_create(&tid2, NULL, task2, NULL) != 0) { printf("tid2 create error\n"); return -1; } if(pthread_create(&tid3, NULL, task2, NULL) != 0) { printf("tid2 create error\n"); return -1; } if(pthread_create(&tid4, NULL, task2, NULL) != 0) { printf("tid2 create error\n"); return -1; } printf("主线程:tid1 = %#x, tid2 = %#x, tid3 = %#x, tid4 = %#x\n", tid1, tid2, tid3, tid4); pthread_join(tid1, NULL); pthread_join(tid2, NULL); pthread_join(tid3, NULL); pthread_join(tid4, NULL); pthread_cond_destroy(&cond); pthread_mutex_destroy(&mutex); std::cout << "Hello, World!" << std::endl; return 0; }
|
C++11中的多线程
1> C++11之后支持线程支持库
2> C++也支持线程创建、互斥锁、条件变量
3> 线程支持库需要引入头文件 #include<thread>,本质上是对pthread的封装,链接时还是需要带上-pthread
详情可以参考C++中文 - API参考文档
线程相关操作
1> 线程的创建:C++线程支持库,本质是是面向对象的操作
2> 创建线程:可以使用构造函数完成
线程体函数种类:
1. 可以是任意类型的函数,不必要是 `void *` 类型参数也是`void *`类型
2. 可以是全局函数,也可以是类中成员函数当做线程体
3. 可以是仿函数当作线程体函数
4. 也可以是Lambda表达式当作线程体函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| #include <myhead.h> #include<thread>
void ThreadFun_1() { cout<<"ThreadFun_1 tid = "<< this_thread::get_id()<<endl; cout << "ThreadFun_1 test"<<endl; }
void ThreadFun_2(int num, string str) { cout<<"ThreadFun_2 tid = "<< this_thread::get_id()<<endl; cout<<"num = "<<num<<" str = "<<str<<endl; }
class ThreadClass { public: string name; int age; void ThreadClassFun() { cout<<"ThreadFun_3 tid = "<< this_thread::get_id()<<endl; cout << "name = "<<this->name<<" age = "<<age<<endl; } };
int main(int argc, const char *argv[]) { thread th1(ThreadFun_1); 为无参无返回值函数 string name = "zpp"; thread th2(ThreadFun_2, 520, name); ThreadClass test; test.name = "zhangsan"; test.age = 18; thread th3(&ThreadClass::ThreadClassFun, &test);
thread th4([](int key){ cout<<"ThreadFun_4 tid = "<< this_thread::get_id()<<endl; cout<<"key = "<<key<<endl; }, 999); th1.join(); th2.join(); th3.join(); th4.join(); std::cout << "Hello, World!" << std::endl; return 0; }
|
3> 线程号获取:this_thread::get_id()
延时:this_thread::sleep_for()
1 2 3 4 5 6 7
| namespace this_thread { get_id(); sleep_for(); sleep_until(); yield(); }
|
4> 有关线程的回收
1、阻塞方式回收线程:std::thread::join();
2、非阻塞形式回收线程:std::thread::detach();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <myhead.h> #include <thread>
void ThreadFun_1() { for (int i = 0; i < 10; i++) { cout << "ThreadFun_1 tid = " << this_thread::get_id() << endl; cout << "ThreadFun_1 test" << endl; this_thread::sleep_for(1s); } }
int main(int argc, const char *argv[]) { thread th1(ThreadFun_1); th1.detach(); std::cout << "Hello, World!" << std::endl; this_thread::sleep_for(20s); return 0; }
|
互斥锁的使用
1> 互斥锁本质上是完成将多个线程使用临界资源时,防止竞态
2> 需要引入头文件 #include<mutex>
3> 常用函数
- 构造函数:创建一个互斥锁对象
- lock():上锁
- unlock()释放锁资源
注意:通常不直接使用 std::mutex : std::unique_lock 、 std::lock_guard 或 std::scoped_lock (C++17 起)以更加异常安全的方式管理锁定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include <myhead.h> #include <thread> #include<mutex>
mutex mux;
void ThreadFun_1() { mux.lock(); cout<<"======================================"<<endl; cout<<"tid = "<<this_thread::get_id()<<endl; this_thread::sleep_for(1s); cout<<"**************************************"<<endl; mux.unlock(); }
int main(int argc, const char *argv[]) { for(int i=0; i<10; i++) { thread th(ThreadFun_1); th.detach(); } this_thread::sleep_for(20s); std::cout << "Hello, World!" << std::endl; this_thread::sleep_for(20s); return 0; }
|
条件变量
1> 实现一个生产者对应多个消费者问题
2> 需要引入头文件:#include<condition_variable>
3> 常用函数
构造函数:创建并初始化一个条件变量
wait(lock):将消费者线程放入等待队列中
唤醒线程:
cv.notify_one(); 唤醒一个线程
cv.notify_all(); 唤醒所有线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include <myhead.h> #include<condition_variable> #include<thread> #include<mutex>
condition_variable cv; mutex mux;
void ThreadWrite() { for(int i=0; i<5; i++) { this_thread::sleep_for(2s); cout<<"我生产了一辆特斯拉"<<endl; cv.notify_one(); } }
void ThreadRead() { unique_lock<mutex> lock(mux); cv.wait(lock); cout<<"我消费了一辆特斯拉"<<endl; lock.unlock(); } int main(int argc, const char *argv[]) { thread th1(ThreadWrite); for(int i=0; i<5; i++) { thread th2(ThreadRead); th2.detach(); } th1.join(); std::cout << "Hello, World!" << std::endl; return 0; }
|