昨天面了个大厂,自己太傻逼了,这么简单的一道题目没写出来。因为我不太熟悉条件变量,拖了很长时间,面试官估计有点失望,后续的八股也没问什么,意料之中的挂了😭。
还有个题目是LRU,这个倒是临时抱佛脚复习到了,写出来了。

题目
问题:线程1打印1,线程2打印2,线程3打印3,线程1打印4,线程2打印5,线程3打印6……一直打印到100。
其实一点都不难,就是我自己平时压根没做过多少线程同步的练习,对于条件变量之类的玩意基本停留在理论和学习时的简单demo层面,一上战场加上紧张就毛都不会了。
思路1-直接用线程序号做判断
最简单的一个思路,因为只有三个线程,每个线程打印的数字是有规律的
- 线程一打印的数字%3都等于1
- 线程二打印的数字%3都等于2
- 线程三打印的数字%3都等于0
所以我们可以直接写一个函数,提供一个线程编号,通过计算判断当前是否是自己需要打印的数字,不是就睡觉。这个方法是最简单的,只需要一个全局变量加一个锁就能实现。
两个atomic变量,一个用于主线程通知所有线程当前已经初始化完毕,另外一个用于线程通知主线程当前已经执行完毕。
| 12
 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
 
 | #include <thread>#include <mutex>
 #include <atomic>
 #include <iostream>
 
 
 std::atomic<bool> isRun = false;
 std::atomic<bool> isFinished = false;
 std::mutex gMtx;
 int count = 1;
 
 void PrintCountFun(int no)
 {
 while(!isRun)
 {
 std::this_thread::sleep_for(std::chrono::microseconds(10));
 }
 
 while(!isFinished)
 {
 gMtx.lock();
 if(count > 100)
 {
 isFinished = true;
 gMtx.unlock();
 return;
 }
 
 if(no!=3 && count % 3 == no)
 {
 std::cout << no << " - " << std::this_thread::get_id() << " - c: " << count << std::endl;
 count ++;
 gMtx.unlock();
 continue;
 }
 
 if(no == 3 && count % 3 == 0)
 {
 std::cout << no << " - " << std::this_thread::get_id() << " - c: " << count << std::endl;
 count ++;
 gMtx.unlock();
 continue;
 }
 gMtx.unlock();
 std::this_thread::sleep_for(std::chrono::microseconds(10));
 }
 }
 
 int main()
 {
 std::thread t1(PrintCountFun,1);
 std::thread t2(PrintCountFun,2);
 std::thread t3(PrintCountFun,3);
 
 t1.detach();
 t2.detach();
 t3.detach();
 
 isRun = true;
 while(!isFinished)
 {
 std::this_thread::sleep_for(std::chrono::microseconds(100));
 }
 std::cout << "finished\n";
 return 0;
 }
 
 | 
方法1代码运行结果
反正是通过编号判断的,肯定不会错,只不过这样效率其实很低。
| 12
 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
 
 | 1 - 140173660395072 - c: 12 - 140173652002368 - c: 2
 3 - 140173643609664 - c: 3
 1 - 140173660395072 - c: 4
 2 - 140173652002368 - c: 5
 3 - 140173643609664 - c: 6
 1 - 140173660395072 - c: 7
 2 - 140173652002368 - c: 8
 3 - 140173643609664 - c: 9
 1 - 140173660395072 - c: 10
 2 - 140173652002368 - c: 11
 3 - 140173643609664 - c: 12
 1 - 140173660395072 - c: 13
 2 - 140173652002368 - c: 14
 3 - 140173643609664 - c: 15
 1 - 140173660395072 - c: 16
 2 - 140173652002368 - c: 17
 3 - 140173643609664 - c: 18
 1 - 140173660395072 - c: 19
 2 - 140173652002368 - c: 20
 3 - 140173643609664 - c: 21
 1 - 140173660395072 - c: 22
 2 - 140173652002368 - c: 23
 3 - 140173643609664 - c: 24
 1 - 140173660395072 - c: 25
 2 - 140173652002368 - c: 26
 3 - 140173643609664 - c: 27
 1 - 140173660395072 - c: 28
 2 - 140173652002368 - c: 29
 3 - 140173643609664 - c: 30
 1 - 140173660395072 - c: 31
 2 - 140173652002368 - c: 32
 3 - 140173643609664 - c: 33
 1 - 140173660395072 - c: 34
 2 - 140173652002368 - c: 35
 3 - 140173643609664 - c: 36
 1 - 140173660395072 - c: 37
 2 - 140173652002368 - c: 38
 3 - 140173643609664 - c: 39
 1 - 140173660395072 - c: 40
 2 - 140173652002368 - c: 41
 3 - 140173643609664 - c: 42
 1 - 140173660395072 - c: 43
 2 - 140173652002368 - c: 44
 3 - 140173643609664 - c: 45
 1 - 140173660395072 - c: 46
 2 - 140173652002368 - c: 47
 3 - 140173643609664 - c: 48
 1 - 140173660395072 - c: 49
 2 - 140173652002368 - c: 50
 3 - 140173643609664 - c: 51
 1 - 140173660395072 - c: 52
 2 - 140173652002368 - c: 53
 3 - 140173643609664 - c: 54
 1 - 140173660395072 - c: 55
 2 - 140173652002368 - c: 56
 3 - 140173643609664 - c: 57
 1 - 140173660395072 - c: 58
 2 - 140173652002368 - c: 59
 3 - 140173643609664 - c: 60
 1 - 140173660395072 - c: 61
 2 - 140173652002368 - c: 62
 3 - 140173643609664 - c: 63
 1 - 140173660395072 - c: 64
 2 - 140173652002368 - c: 65
 3 - 140173643609664 - c: 66
 1 - 140173660395072 - c: 67
 2 - 140173652002368 - c: 68
 3 - 140173643609664 - c: 69
 1 - 140173660395072 - c: 70
 2 - 140173652002368 - c: 71
 3 - 140173643609664 - c: 72
 1 - 140173660395072 - c: 73
 2 - 140173652002368 - c: 74
 3 - 140173643609664 - c: 75
 1 - 140173660395072 - c: 76
 2 - 140173652002368 - c: 77
 3 - 140173643609664 - c: 78
 1 - 140173660395072 - c: 79
 2 - 140173652002368 - c: 80
 3 - 140173643609664 - c: 81
 1 - 140173660395072 - c: 82
 2 - 140173652002368 - c: 83
 3 - 140173643609664 - c: 84
 1 - 140173660395072 - c: 85
 2 - 140173652002368 - c: 86
 3 - 140173643609664 - c: 87
 1 - 140173660395072 - c: 88
 2 - 140173652002368 - c: 89
 3 - 140173643609664 - c: 90
 1 - 140173660395072 - c: 91
 2 - 140173652002368 - c: 92
 3 - 140173643609664 - c: 93
 1 - 140173660395072 - c: 94
 2 - 140173652002368 - c: 95
 3 - 140173643609664 - c: 96
 1 - 140173660395072 - c: 97
 2 - 140173652002368 - c: 98
 3 - 140173643609664 - c: 99
 1 - 140173660395072 - c: 100
 finished
 
 | 
 
我刚开始的时候就是想着用条件变量,比如三个条件变量分别用于通知……但这个实在是复杂且没有必要,没做过条件变量练习一时半会完全是写不出来的。后来就换了这个思路,但时间已经来不及了。
说来惭愧,我写完上面代码的基本框架后编译错误,显示atomic<bool>初始化失败,我找了好久都没有发现是自己没有引用<atomic>这个头文件导致的,一直在看自己的语法是不是写错了……
还是自己练习不够,不然也不会怀疑自己语法是否写错😣
思路2-条件变量
上述的这个单锁加遍历的思路其实并不是一个好的答案(但总比没写出来的好),因为它完全没有涉及到线程同步的机制,三个线程其实毫无关系,只是在通过锁竞争和判断来确定自己是否需要打印。面试官想要的答案肯定得包含条件变量。
下面这个解决方案的思路同样是通过线程编号判断(只不过拆分成了三个不同的函数),每个线程打印完毕自己的,就把另外两个线程唤醒,然后被唤醒的两个线程会通过锁竞争先后进入自己的函数打印区域,判断是否是自己需要打印的部分,如果不是就继续在条件变量里面等待。
| 12
 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
 
 | #include <mutex>#include <iostream>
 #include <thread>
 #include <condition_variable>
 
 std::mutex gMtx;
 std::condition_variable cv;
 int count = 1;
 
 void func1()
 {
 std::unique_lock<std::mutex> lc(gMtx);
 while(count <= 100)
 {
 while (count % 3 == 0 && count <= 100)
 {
 std::cout << std::this_thread::get_id() << " - c: " << count << std::endl;
 count++;
 cv.notify_all();
 }
 cv.wait(lc);
 }
 cv.notify_all();
 }
 
 void func2()
 {
 std::unique_lock<std::mutex> lc(gMtx);
 while (count <= 100)
 {
 while (count % 3 == 1 && count <= 100)
 {
 std::cout << std::this_thread::get_id() << " - c: " << count << std::endl;
 count++;
 cv.notify_all();
 }
 cv.wait(lc);
 }
 }
 
 void func3()
 {
 std::unique_lock<std::mutex> lc(gMtx);
 while (count <= 100)
 {
 while (count % 3 == 2 && count <= 100)
 {
 std::cout << std::this_thread::get_id() << " - c: " << count << std::endl;
 count++;
 cv.notify_all();
 }
 cv.wait(lc);
 }
 }
 
 
 int main()
 {
 std::thread s1(func1);
 std::thread s2(func2);
 std::thread s3(func3);
 
 s1.join();
 s2.join();
 s3.join();
 std::cout << "finished\n";
 return 0;
 }
 
 | 
方法2代码运行结果
| 12
 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
 
 | 140713900033600 - c: 1140713891640896 - c: 2
 140713908426304 - c: 3
 140713900033600 - c: 4
 140713891640896 - c: 5
 140713908426304 - c: 6
 140713900033600 - c: 7
 140713891640896 - c: 8
 140713908426304 - c: 9
 140713900033600 - c: 10
 140713891640896 - c: 11
 140713908426304 - c: 12
 140713900033600 - c: 13
 140713891640896 - c: 14
 140713908426304 - c: 15
 140713900033600 - c: 16
 140713891640896 - c: 17
 140713908426304 - c: 18
 140713900033600 - c: 19
 140713891640896 - c: 20
 140713908426304 - c: 21
 140713900033600 - c: 22
 140713891640896 - c: 23
 140713908426304 - c: 24
 140713900033600 - c: 25
 140713891640896 - c: 26
 140713908426304 - c: 27
 140713900033600 - c: 28
 140713891640896 - c: 29
 140713908426304 - c: 30
 140713900033600 - c: 31
 140713891640896 - c: 32
 140713908426304 - c: 33
 140713900033600 - c: 34
 140713891640896 - c: 35
 140713908426304 - c: 36
 140713900033600 - c: 37
 140713891640896 - c: 38
 140713908426304 - c: 39
 140713900033600 - c: 40
 140713891640896 - c: 41
 140713908426304 - c: 42
 140713900033600 - c: 43
 140713891640896 - c: 44
 140713908426304 - c: 45
 140713900033600 - c: 46
 140713891640896 - c: 47
 140713908426304 - c: 48
 140713900033600 - c: 49
 140713891640896 - c: 50
 140713908426304 - c: 51
 140713900033600 - c: 52
 140713891640896 - c: 53
 140713908426304 - c: 54
 140713900033600 - c: 55
 140713891640896 - c: 56
 140713908426304 - c: 57
 140713900033600 - c: 58
 140713891640896 - c: 59
 140713908426304 - c: 60
 140713900033600 - c: 61
 140713891640896 - c: 62
 140713908426304 - c: 63
 140713900033600 - c: 64
 140713891640896 - c: 65
 140713908426304 - c: 66
 140713900033600 - c: 67
 140713891640896 - c: 68
 140713908426304 - c: 69
 140713900033600 - c: 70
 140713891640896 - c: 71
 140713908426304 - c: 72
 140713900033600 - c: 73
 140713891640896 - c: 74
 140713908426304 - c: 75
 140713900033600 - c: 76
 140713891640896 - c: 77
 140713908426304 - c: 78
 140713900033600 - c: 79
 140713891640896 - c: 80
 140713908426304 - c: 81
 140713900033600 - c: 82
 140713891640896 - c: 83
 140713908426304 - c: 84
 140713900033600 - c: 85
 140713891640896 - c: 86
 140713908426304 - c: 87
 140713900033600 - c: 88
 140713891640896 - c: 89
 140713908426304 - c: 90
 140713900033600 - c: 91
 140713891640896 - c: 92
 140713908426304 - c: 93
 140713900033600 - c: 94
 140713891640896 - c: 95
 140713908426304 - c: 96
 140713900033600 - c: 97
 140713891640896 - c: 98
 140713908426304 - c: 99
 140713900033600 - c: 100
 finished
 
 | 
 
 
The end
自己平时不多练习,只能大意失荆州了……