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

还有个题目是LRU,这个倒是临时抱佛脚复习到了,写出来了。

题目

问题:线程1打印1,线程2打印2,线程3打印3,线程1打印4,线程2打印5,线程3打印6……一直打印到100。

其实一点都不难,就是我自己平时压根没做过多少线程同步的练习,对于条件变量之类的玩意基本停留在理论和学习时的简单demo层面,一上战场加上紧张就毛都不会了。

思路1-直接用线程序号做判断

最简单的一个思路,因为只有三个线程,每个线程打印的数字是有规律的

  • 线程一打印的数字%3都等于1
  • 线程二打印的数字%3都等于2
  • 线程三打印的数字%3都等于0

所以我们可以直接写一个函数,提供一个线程编号,通过计算判断当前是否是自己需要打印的数字,不是就睡觉。这个方法是最简单的,只需要一个全局变量加一个锁就能实现。

两个atomic变量,一个用于主线程通知所有线程当前已经初始化完毕,另外一个用于线程通知主线程当前已经执行完毕。

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
#include <thread>
#include <mutex>
#include <atomic>
#include <iostream>

// 线程1打印1,线程2打印2,线程3打印3,线程1打印4,线程2打印5.... 打印到100
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;
}
// 1号2号线程
if(no!=3 && count % 3 == no)
{
std::cout << no << " - " << std::this_thread::get_id() << " - c: " << count << std::endl;
count ++;
gMtx.unlock();
continue;
}
// 3号线程
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代码运行结果

反正是通过编号判断的,肯定不会错,只不过这样效率其实很低。

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
1 - 140173660395072 - c: 1
2 - 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-条件变量

上述的这个单锁加遍历的思路其实并不是一个好的答案(但总比没写出来的好),因为它完全没有涉及到线程同步的机制,三个线程其实毫无关系,只是在通过锁竞争和判断来确定自己是否需要打印。面试官想要的答案肯定得包含条件变量。

下面这个解决方案的思路同样是通过线程编号判断(只不过拆分成了三个不同的函数),每个线程打印完毕自己的,就把另外两个线程唤醒,然后被唤醒的两个线程会通过锁竞争先后进入自己的函数打印区域,判断是否是自己需要打印的部分,如果不是就继续在条件变量里面等待。

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
#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代码运行结果
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
140713900033600 - c: 1
140713891640896 - 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

自己平时不多练习,只能大意失荆州了……