0%

腾讯课堂-C++11多线程

线程管理

多线程的执行函数尽量用传引用,以减少不必要资源开销;根据hardware_concurrency得到核心数来决定开多线程的数量;

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
#include <iostream>
#include <thread>
#include <unistd.h>
using namespace std;

// 传入操作类,重载运算符()
class Fctor{
public: // 参数使用引用减少多线程资源拷贝
void operator()(string& msg) // opt: string msg
{
cout <<"from f1:" << msg<<endl;
msg = "changed";
}
};

int main()
{
string s = "not changed";
cout << this_thread::get_id()<<endl;
thread t1((Fctor()), ref(s)); // move(s),此时最后输出的s为空。
t1.join();

cout << s << endl;
cout << thread::hardware_concurrency() << endl; //当前设备核心数

return 0;
}

// output
// thread_id
// from f1:not changed
// not changed

数据竞争及互斥对象

避免数据竞争,用lock_guard解决抛出异常的问题;lock_guard保证线程独占资源而安全

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
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <fstream>
using namespace std;

// lock_guard构建f单例,保证独占fout资源,从而保证线程安全
class LofFile
{
public:
LofFile(){
f.open("log.txt");
}
void shared_print(string id, int value)
{
lock_guard<mutex> locker(m_mutex);
f<< "From" << id <<": " << value << endl;
}

private:
mutex m_mutex;
ofstream f;
};
/* cout资源为全局所有,即使加入互斥对象依然不能完全保护
mutex mu;
void shared_print(string msg, int id)
{
lock_guard<mutex> guard(mu);
// mu.lock();
cout << msg << id << endl;
// mu.unlock();
}
*/


void function_1(LofFile& log)
{
for(int i=0; i>-100; i--)
log.shared_print("From t1:",i);
}
int main()
{
LofFile log;
thread t1(function_1, ref(log));

for(int i=0; i< 100; i++)
log.shared_print("from main: ", i);

t1.join();
return 0;
}

死锁

避免死锁:

  • 评估线程是否需要两个或以上的互斥对象
  • 避免在锁住资源的同时去调用一些不熟悉的函数
  • 使用lock函数帮助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
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <fstream>
using namespace std;

// 循环依赖产生的死锁
class LofFile
{
public:
LofFile() {}

void shared_print(string id, int value)
{
lock(m_mutex, m_mutex2);
lock_guard<mutex> locker(m_mutex, adopt_lock);
lock_guard<mutex> locker2(m_mutex2, adopt_lock);
cout << "From" << id << ": " << value << endl;
}
void shared_print2(string id, int value)
{
lock(m_mutex, m_mutex2); // 用lock确保互斥顺序一致

lock_guard<mutex> locker2(m_mutex2, adopt_lock); // 若调换,则可避免
lock_guard<mutex> locker(m_mutex, adopt_lock);
cout << "From" << id << ": " << value << endl;
}

private:
mutex m_mutex;
mutex m_mutex2;
ofstream f; // 被m_mutex保护的对象
};

void function_1(LofFile &log)
{
for (int i = 0; i > -100; i--)
log.shared_print("From t1:", i);
}
int main()
{
LofFile log;
thread t1(function_1, ref(log));

for (int i = 0; i < 100; i++)
log.shared_print2("from main: ", i);

t1.join();
return 0;
}

Unique Lock和lazy Initialization

加锁的方式:

  1. lock_guard
  2. mutex.lock
  3. unique_lock(推荐,但占用更多系统资源)
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
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <fstream>
using namespace std;

// 所占用的资源
class LofFile
{
public:
LofFile(){
f.open("log.txt");
}
void shared_print(string id, int value)
{
// lock_guard<mutex> locker(m_mutex);
unique_lock<mutex> locker(m_mutex, defer_lock); // unique_lock占用更多系统资源
// do_sth() unlock
// ...

locker.lock();
f << id <<": " << value << endl;
locker.unlock();
// ...


locker.lock(); // another unique_lock

unique_lock<mutex> locker2 = move(locker);

}

private:
mutex m_mutex;
ofstream f;
};


void function_1(LofFile& log)
{
for(int i=0; i>-100; i--)
log.shared_print("From t1:",i);
}
int main()
{
LofFile log;
thread t1(function_1, ref(log));

for(int i=0; i< 100; i++)
log.shared_print("From main: ", i);

t1.join();
return 0;
}

生产者和消费者问题

结合unique_lock和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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <functional>
#include <iostream>
#include <fstream>
#include <string>
#include <mutex>
#include <thread>
#include <deque>
#include <condition_variable>

using namespace std;

deque<int> q;
mutex mu;
condition_variable cond; // 条件变量
// 生产者
void function_1()
{
int count = 10;
while(count > 0)
{
unique_lock<mutex> locker(mu);
q.push_front(count);
locker.unlock();
cond.notify_one();
this_thread::sleep_for(chrono::seconds(1));
count--;
}
}

// 消费者:条件变量
void function_2()
{
int data = 0;
while(data!=1)
{
unique_lock<mutex> locker(mu);
cond.wait(locker, [](){ return !q.empty(); }); // 只能搭配unique_lock使用,用Lambda函数避免自解锁
data = q.back();
q.pop_back();
locker.unlock();
cout <<"t2 got a value from t1: " << data<<endl;
}
}

/* 不优雅
void function_2()
{
int data = 0;
while(data!=1)
{
unique_lock<mutex> locker(mu);
if(!q.empty())
{
data = q.back();
q.pop_back();
locker.unlock();
cout <<"t2 got a value from t1: " << data<<endl;
}
else {
locker.unlock();
// this_thread::sleep_for(chrono::milliseconds(10)); 若无数据,一直解锁locker;此时加入sleep缓解不停释放的行为
}
}
}
*/

int main()
{
thread t1(function_1);
thread t2(function_2);
t1.join();
t2.join();
return 0;

}