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
#include <iostream>
#include <vector>
using namespace std;

// TODO ============================ [左值 右值 引用]
class Student {
private:
string info = "AAA"; // 旧变量

// TODO 第一种情况【getInfo函数的info 与 main函数的result 是旧与新的两个变量而已,他们是值传递,所以右值修改时,影响不了里面的旧变量】
public:
string getInfo() {
return this->info;
}

// TODO 第二种情况【getInfo函数的info 与 main函数的result 是引用关系,一块内存空间 有多个别名而已,所以右值修改时,直接影响旧变量】
public:
// 返回引用可以直接修改
string & getInfo_front() {
return this->info;
}
};

int main() {
/*vector<int> v;
int r = v.front(); // 左值 获取
v.front() = 88; // 右值 修改*/

Student student;

// TODO 第一种情况
student.getInfo() = "九阳神功";
string result = student.getInfo();
cout << "第一种情况:" << result << endl;

// TODO 第二种情况
student.getInfo_front() = "三分归元气"; // 右值 修改内容
result = student.getInfo_front(); // 左值 获取内容
cout << "第二种情况:" << result << endl;
}

2. thread

thread是C++ 11引入的一个线程类,主要也是封装pthread类,用起来更简单,但是阉割了部分功能,日常开发中主要还是使用pthread。

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
#include <iostream>
#include <thread>
#include <unistd.h>

using namespace std;

// 异步线程 子线程
void runAction(int number) { // 相当于 Java的 run函数一样
for (int i = 0; i < 10; ++i) {
cout << "runAction:" << number << endl;
sleep(1);
}
}

// main函数的线程
int main() {
// TODO 方式一 main只等3秒钟,
/*thread thread1(runAction, 100);

// sleep(3); // 我只等你三秒钟
cout << "main弹栈了" << endl;*/

// TODO 方式二 我等你执行完成后,我再执行
thread thread2(runAction, 100);
thread2.join(); // 我等runAction执行完成后,我再继续执行下面代码..
cout << "main弹栈了" << endl;
/*
runAction:100
runAction:100
runAction:100
runAction:100
runAction:100
runAction:100
runAction:100
runAction:100
runAction:100
runAction:100
main弹栈了
*/

return 0;
}

3. pthread用法

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
#include <iostream>
#include <pthread.h> // Cygwin 有 pthreads支持
using namespace std;

// void *(*)(void *)
void * customPthreadTask(void * pVoid) { // 异步线程 相当于Java的Thread.run函数一样
// C++转换static_cast 转换指针操作的
int * number = static_cast<int *>(pVoid); // pVoid==number int的地址,所以我用int*接收,很合理
cout << "异步线程执行了:" << *number << endl;

return 0; // 坑 坑 坑,必须返回,否则有错误,不好查询
}

int main() {
int number = 9527;

/**
int pthread_create (pthread_t *, // 参数一:线程ID
const pthread_attr_t *, // 参数二:线程属性
void *(*)(void *), // 参数三:函数指针的规则
void *); // 参数四:给函数指针传递的内容,void * 可以传递任何内容
*/
pthread_t pthreadID; // 线程ID,每个线程都需要有的线程ID

pthread_create(&pthreadID, 0, customPthreadTask, &number);

return 0;
}

4. pthread的三种状态

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
//  pthread 的 三种情况分析 的
// 第一种情况,main函数只要结束,不等异步线程,全部结束
// 第二种情况,我们开发者,千万不要让 main函数睡眠的方式,去等待异步线程
// 第三种情况,main函数一直等待 异步线程,只有异步线程执行完成后,我在执行 join后面的代码
#include <iostream>
#include <pthread.h> // Derry Cygwin 有 pthreads支持
#include <unistd.h>

using namespace std;

// void *(*)(void *)
void * runTask(void * pVoid) { // 异步线程 子线程
int number = *static_cast<int *>(pVoid);
cout << "异步线程执行了:" << number << endl;

for (int i = 0; i < 10; ++i) {
cout << "run:" << i << endl;
sleep(1);
}

return 0;
}

int main() {
int number = 999;

pthread_t pthreadID;
pthread_create(&pthreadID, 0, runTask, &number);

// 为了演示第二种情况
// sleep(3); // main函数只 异步线程三秒钟

// 异步线程在执行的过程中,我们的main函数 相当于 阻塞在这里不动了,只有异步线程执行完成后,我才开始执行join后面的代码
pthread_join(pthreadID, 0);

cout << "main函数即将弹栈..." << endl;
return 0;
}

5. 分离线程与非分离线程区别

  • 分离线程: 各个线程都是自己运行自己的,老死不相往来,例如:main函数结束,全部结束,不会等待异步线程 【多线程独立计算情况下场景】
  • 非分离线程: 线程有协作的能力,例如:main函数线程会等待 异步线程执行完成后,我再执行 后面main函数的代码【协作,顺序执行 场景】
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 <iostream>
#include <pthread.h> // Derry Cygwin 有 pthreads支持
#include <unistd.h>

using namespace std;

// void *(*)(void *)
void * runTask(void * pVoid) { // 异步线程 子线程
int number = *static_cast<int *>(pVoid);
cout << "异步线程执行了:" << number << endl;

for (int i = 0; i < 10; ++i) {
cout << "run:" << i << endl;
sleep(1);
}
return 0;
}

int main() {
int number = 999;

pthread_t pthreadID; // Cygwin允许有野指针
pthread_create(&pthreadID, 0, runTask, &number);

pthread_join(pthreadID, 0);

cout << "main函数即将弹栈..." << endl;
return 0;
}

6. 互斥锁

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 <iostream>
#include <pthread.h>
#include <queue>
#include <unistd.h> // sleep(秒)

using namespace std;

queue<int> queueData; // 定义一个全局的队列,用于 存储/获取

pthread_mutex_t mutex; // 定义一个互斥锁,注意:(Cygwin平台 此互斥锁,不能有野指针,坑)

// void *(*)(void *)
void *task(void *pVoid) {

/*synchronize(锁) {
// code
}*/

pthread_mutex_lock(&mutex); // 锁住

cout << "异步线程-当前线程的标记是:" << *static_cast<int *>(pVoid) << "异步线程" << endl;

if (!queueData.empty()) { // 有元素
printf("异步线程-获取队列的数据:%d\n", queueData.front());
queueData.pop(); // 把数据弹出去,删除的意思
} else { // 没有元素
printf("异步线程-队列中没有数据了\n");
}

sleep(0.2);

pthread_mutex_unlock(&mutex); // 解锁

return 0;
}

int main() {
// 初始化 互斥锁
pthread_mutex_init(&mutex, NULL);

// 给队列 初始化数据 手动增加数据进去
for (int i = 10001; i < 10011; ++i) {
queueData.push(i);
}

// 一次性定义10个线程
pthread_t pthreadIDArray[10];
for (int i = 0; i < 10; ++i) {
pthread_create(&pthreadIDArray[i], 0, task, &i);

// 不能使用 join,如果使用(就变成顺序的方式,就没有多线程的意义了,所以不能写join)
// pthread_join(pthreadIDArray[i], 0);
}

// main函数等 异步线程
sleep(12);

// 销毁 互斥锁
pthread_mutex_destroy(&mutex);
cout << "main函数即将弹栈..." << endl;

// 每次运行 效果都不同:1,8,9,10,3,2,5,8
// 每次运行 效果都是错乱

return 0;
}

7. 多线程通知机制实现生产者消费者模型

建立一个队列,在get(),set()方法中使用互斥锁对队列存元素和元素进行加锁。

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
// 数据工程实现
#ifndef CPPCLIONPROJECT_SAFE_QUEUE_TOO_H
#define CPPCLIONPROJECT_SAFE_QUEUE_TOO_H

#endif //CPPCLIONPROJECT_SAFE_QUEUE_TOO_H

#pragma once // 防止重复写 include 的控制

#include <iostream>
#include <string>
#include <pthread.h>
#include <string>
#include <queue>

using namespace std;

// 定义模版函数 int double float == Java的泛型
template<typename T>

class SafeQueueClass {
private:
queue<T> queue; // 定义队列
pthread_mutex_t mutex; // 定义互斥锁(不允许有野指针)
pthread_cond_t cond; // 条件变量,为了实现 等待 读取 等功能 (不允许有野指针)

public:
SafeQueueClass() {
// 初始化 互斥锁
pthread_mutex_init(&mutex, 0);

// 初始化 条件变量
pthread_cond_init(&cond, 0);
}
~SafeQueueClass() {
// 回收 互斥锁
pthread_mutex_destroy(&mutex);

// 回收 条件变量
pthread_cond_destroy(&cond);
}

// TODO 加入到队列中(进行生成)
void add(T t) {
// 为了安全 加锁
pthread_mutex_lock(&mutex);

queue.push(t); // 把数据加入到队列中

// 告诉消费者,我已经生产好了
// pthread_cond_signal(&cond) // Java notify 单个的
pthread_cond_broadcast(&cond); // Java notifyAll 所有的的

cout << "add queue.push 我已经notifyAll所有等待线程了" << endl;

// 解锁
pthread_mutex_unlock(&mutex);
}

// TODO 从队列中获取(进行消费) 外面的人消费 你可以直接返回,你也可以采用引用
void get(T & t) {
// 为了安全 加锁
pthread_mutex_lock(&mutex);

while (queue.empty()) {
cout << "get empty 我已经乖乖等待中.." << endl;
pthread_cond_wait(&cond, &mutex); // 相当于 Java的 wait 等待了[有可能被系统唤醒]
}

// 证明被唤醒了
t = queue.front(); // 得到 队列中的元素数据 仅此而已
queue.pop(); // 删除元素

// 解锁
pthread_mutex_unlock(&mutex);
}
};
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
#pragma once

#include <iostream>

#include "safe_queue_too.h"
using namespace std;
SafeQueueClass<int> sq;

// TODO 模拟演示 消费者
void * getMethod(void *) {
while (true) {
printf("getMethod\n");

int value;
sq.get(value);
printf("消费者get 得到的数据:%d\n", value);

// 你只要传入 -1 就结束当前循环
if (-1 == value) {
printf("消费者get 全部执行完毕\n");
break;
}
}
return 0;
}

// TODO 模拟演示 生产者
void * setMethod(void *) {
while (true) {
printf("setMethod\n");

int value;
printf("请输入你要生成的信息:\n");
cin >> value;

// 你只要传入 -1 就结束当前循环
if (-1 == value) {
sq.add(value); // 为了让消费者 可以结束循环
printf("消费者get 全部执行完毕\n");
break;
}

sq.add(value);
}
return 0;
}

int main() {
pthread_t pthreadGet;
//分别新建一个生产者消费者线程
pthread_create(&pthreadGet, 0, getMethod, 0);
// pthread_join(pthreadGet, 0); 不能这样写,否则,下面的代码,可能无法有机会执行

pthread_t pthreadSet;
pthread_create(&pthreadSet, 0, setMethod, 0);

pthread_join(pthreadGet, 0);

pthread_join(pthreadSet, 0);

return 0;
}