【学习点滴】c++ 三个线程循环打印ABC

用上了实验室新买的服务器,美滋滋
小试牛刀一番

#include #includeusing namespace std; pthread_mutex_t mutex; int k=0; //全局区变量是共享的哦 pthread_cond_t cond =PTHREAD_COND_INITIALIZER; void *func1(void *arg){ for(int i = 0 ; i<10; i++){ pthread_mutex_lock(&mutex); while(k%3!=0){ pthread_cond_wait(&cond,&mutex); } cout<<"A"; k++; pthread_mutex_unlock(&mutex); pthread_cond_broadcast(&cond); } }void *func2(void *arg){ for(int i = 0 ; i<10; i++){ pthread_mutex_lock(&mutex); while(k%3!=1){ pthread_cond_wait(&cond,&mutex); } cout<<"B"; k++; pthread_mutex_unlock(&mutex); pthread_cond_broadcast(&cond); } }void *func3(void *arg){ for(int i = 0 ; i<10; i++){ pthread_mutex_lock(&mutex); while(k%3!=2){ pthread_cond_wait(&cond,&mutex); } cout<<"C"; k++; pthread_mutex_unlock(&mutex); pthread_cond_broadcast(&cond); } }int main(){pthread_mutex_init(&mutex, NULL); pthread_t tid1; pthread_t tid2; pthread_t tid3; pthread_create(&tid1,NULL,func1,NULL); pthread_create(&tid2,NULL,func2,NULL); pthread_create(&tid3,NULL,func3,NULL); //cout<<"pthread create success!"<

用了mutex和条件变量cond,一个比较简单的多线程程序。
makefile如下:
SOURCE:= $(wildcard *.cpp) OBJS:= $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE)))#在OBJS中 把.cpp和.c的文件变成.o的后缀TARGET:= test CC:= g++ LIBS:= -lpthread #INCLUDE:= -I./usr/local/include/opencv CFLAGS:= -std=c++11 -g -Wall #-O3 #$(INCLUDE) CXXFLAGS:= $(CFLAGS).PHONY : objs clean veryclean rebuild all all : $(TARGET) objs : $(OBJS) rebuild: veryclean all clean : rm -fr *.o veryclean : clean rm -rf $(TARGET)$(TARGET) : $(OBJS)#最终目标 $(CC) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS)

pthread_cond_signal 与 pthread_cond_broadcast:
1,pthread_cond_signal在多处理器上可能同时唤醒多个线程,当你只能让一个线程处理某个任务时,其它被唤醒的线程就需要继续 wait,while循环的意义就体现在这里了,而且规范要求pthread_cond_signal至少唤醒一个pthread_cond_wait上 的线程,其实有些实现为了简单在单处理器上也会唤醒多个线程.
2,某些应用,如线程池,pthread_cond_broadcast唤醒全部线程,但我们通常只需要一部分线程去做执行任务,所以其它的线程需要继续wait.所以强烈推荐此处使用while循环.
其实说白了很简单,就是pthread_cond_signal()也可能唤醒多个线程,而如果你同时只允许一个线程访问的话,就必须要使用while来进行条件判断,以保证临界区内只有一个线程在处理。





c++线程的几种锁:
具体见https://blog.csdn.net/xy_cpp/article/details/81910513
线程之间的锁有:互斥锁、条件锁、自旋锁、读写锁、递归锁。一般而言,锁的功能与性能成反比。不过我们一般不使用递归锁(C++标准库提供了std::recursive_mutex),所以这里就不推荐了。
互斥锁:
在某一时刻,只有一个线程可以获取互斥锁,在释放互斥锁之前其他线程都不能获取该互斥锁。如果其他线程想要获取这个互斥锁,那么这个线程只能以阻塞方式进行等待。
条件锁:
即条件变量,某一个线程因为某个条件为满足时可以使用条件变量使改程序处于阻塞状态。一旦条件满足以“信号量”的方式唤醒一个因为该条件而被阻塞的线程。最为常见就是在线程池中,起初没有任务时任务队列为空,此时线程池中的线程因为“任务队列为空”这个条件处于阻塞状态。一旦有任务进来,就会以信号量的方式唤醒一个线程来处理这个任务。
自旋锁:
在对一个已经被持有的锁进行加锁时,线程不会阻塞,而是原地自旋,始终占用cpu
前面的两种锁是比较常见的锁,也比较容易理解。下面通过比较互斥锁和自旋锁原理的不同,这对于真正理解自旋锁有很大帮助。假设我们有一个两个处理器core1和core2计算机,现在在这台计算机上运行的程序中有两个线程:T1和T2分别在处理器core1和core2上运行,两个线程之间共享着一个资源。
首先我们说明互斥锁的工作原理,互斥锁是是一种sleep-waiting的锁。假设线程T1获取互斥锁并且正在core1上运行时,此时线程T2也想要获取互斥锁(pthread_mutex_lock),但是由于T1正在使用互斥锁使得T2被阻塞。当T2处于阻塞状态时,T2被放入到等待队列中去,处理器core2会去处理其他任务而不必一直等待(忙等)。也就是说处理器不会因为线程阻塞而空闲着,它去处理其他事务去了。
而自旋锁就不同了,自旋锁是一种busy-waiting的锁。也就是说,如果T1正在使用自旋锁,而T2也去申请这个自旋锁,此时T2肯定得不到这个自旋锁。与互斥锁相反的是,此时运行T2的处理器core2会一直不断地循环检查锁是否可用(自旋锁请求),直到获取到这个自旋锁为止。
从“自旋锁”的名字也可以看出来,如果一个线程想要获取一个被使用的自旋锁,那么它会一致占用CPU请求这个自旋锁使得CPU不能去做其他的事情,直到获取这个锁为止,这就是“自旋”的含义。当发生阻塞时,互斥锁可以让CPU去处理其他的任务;而自旋锁让CPU一直不断循环请求获取这个锁。通过两个含义的对比可以我们知道“自旋锁”是比较耗费CPU的。

读写锁:
读锁共享,写锁独占
说到读写锁我们可以借助于“读者-写者”问题进行理解。首先我们简单说下“读者-写者”问题。
【【学习点滴】c++ 三个线程循环打印ABC】计算机中某些数据被多个进程共享,对数据库的操作有两种:一种是读操作,就是从数据库中读取数据不会修改数据库中内容;另一种就是写操作,写操作会修改数据库中存放的数据。因此可以得到我们允许在数据库上同时执行多个“读”操作,但是某一时刻只能在数据库上有一个“写”操作来更新数据。这就是一个简单的读者-写者模型。

    推荐阅读