实现简单的固定线程数的线程池

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
作者:Graphene
链接:https://www.zhihu.com/question/27908489/answer/355105668
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

#include <mutex>
#include <condition_variable>
#include <functional>
#include <queue>
#include <thread>

class fixed_thread_pool {
public:
explicit fixed_thread_pool(size_t thread_count)
: data_(std::make_shared<data>()) {
for (size_t i = 0; i < thread_count; ++i) {
std::thread([data = data_] {
std::unique_lock<std::mutex> lk(data->mtx_);
for (;;) {
if (!data->tasks_.empty()) {
auto current = std::move(data->tasks_.front());
data->tasks_.pop();
lk.unlock();
current();
lk.lock();
} else if (data->is_shutdown_) {
break;
} else {
data->cond_.wait(lk);
}
}
}).detach();
}
}

fixed_thread_pool() = default;
fixed_thread_pool(fixed_thread_pool&&) = default;

~fixed_thread_pool() {
if ((bool) data_) {
{
std::lock_guard<std::mutex> lk(data_->mtx_);
data_->is_shutdown_ = true;
}
data_->cond_.notify_all();
}
}

template <class F>
void execute(F&& task) {
{
std::lock_guard<std::mutex> lk(data_->mtx_);
data_->tasks_.emplace(std::forward<F>(task));
}
data_->cond_.notify_one();
}

private:
struct data {
std::mutex mtx_;
std::condition_variable cond_;
bool is_shutdown_ = false;
std::queue<std::function<void()>> tasks_;
};
std::shared_ptr<data> data_;
};

非常僵硬的看不懂了,之后再来回味。。。。

参考资料

查看更多

实现简单的线程安全Queue

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
#pragma once

#include <mutex>
#include <queue>

// Thread safe implementation of a Queue using an std::queue
template <typename T>
class SafeQueue {
private:
std::queue<T> m_queue;
std::mutex m_mutex;
public:
SafeQueue() {

}

SafeQueue(SafeQueue& other) {
//TODO:
}

~SafeQueue() {

}


bool empty() {
std::unique_lock<std::mutex> lock(m_mutex);
return m_queue.empty();
}

int size() {
std::unique_lock<std::mutex> lock(m_mutex);
return m_queue.size();
}

void enqueue(T& t) {
std::unique_lock<std::mutex> lock(m_mutex);
m_queue.push(t);
}

bool dequeue(T& t) {
std::unique_lock<std::mutex> lock(m_mutex);

if (m_queue.empty()) {
return false;
}
t = std::move(m_queue.front());

m_queue.pop();
return true;
}
};

参考资料

查看更多

实现string类

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
class String
{
public:
String()
:data_(new char[1])
{
*data_ = '\0';
}
String(const char& str)
:data_(new char[strlen(str) + 1])
{
strcpy(data_, str);
}
String(const String& rhs)
:data_(new char[rhs.size() + 1])
{
strcpy(data_, rhs.c_str());
}
~String()
{
delete[] data_;
}
/* traditional
String& operator=(const String& rhs)
{
String tmp(rhs);
swap(tmp);
return *this
}*/

String& operator=(String rhs)
{
swap(rhs);
return *this;
}


// C++ 11
String(String&& rhs)
: data_(rhs.data_)
{
rhs.data_ = nullptr;
}

String& operator=(String&& rhs)
{
swap(rhs);
return *this;
}

size_t size() const
{
return strlen(data_);
}

const char* c_str() const
{
return data_;
}

void swap(String& rhs)
{
std::swap(data_, rhs.data_);
}

private:
char* data_;
}
1
2
3
4
5
String& operator=(String rhs) // yes, pass-by-value
{
swap(rhs);
return *this;
}

使用swap的原因是,这个String类涉及到内存分配操作,这个操作可能会抛出异常;
上面的实现分3步:

  1. 把“源对象”拷贝到参数rhs里
查看更多

Noncopyable

在 Muduo 中,有一个 noncopyable 类,继承了该类的派生类不能被拷贝,只能被移动。涉及拷贝的函数有两个:拷贝构造函数和拷贝复制操作符。将这两个方法声明为不可访问或者删除,就可以达到不可拷贝的效果。

将函数声明为 private

将拷贝构造函数和拷贝复制操作符声明为 private,能阻止编译器创建这两个函数,同时阻止用户调用他们,从而达到阻止拷贝的效果。

1
2
3
4
5
6
7
8
9
class noncopyable
{
protected:
noncopyable() {}
~noncopyable() {}
private:// emphasize the following members are private
noncopyable( const noncopyable& );
noncopyable& operator=( const noncopyable& );
};

使用方法如下:

1
class foo: private noncopyable{};

查看更多

对象池

对象池对于创建开销比较大的对象来说很有意义,为了避免重复创建开销比较大的对象,我们可以通过对象池来优化。对象池的思路比较简单,事先创建好一批对象,放到一个集合中,以后每当程序需要新的对象时候,都从对象池里获取,每当程序用完该对象后,都把该对象归还给对象池。这样会避免重复的对象创建,提高程序性能。

简单实现

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 <list>

template<typename Object>
class ObjectPool
{
public:

ObjectPool(size_t unSize) :
m_unSize(unSize)
{
for (size_t unIdx = 0; unIdx < m_unSize; ++ unIdx) {
m_oPool.push_back(new Object());
}
}

~ObjectPool()
{
typename std::list<Object *>::iterator oIt = m_oPool.begin();
while (oIt != m_oPool.end())
{
delete (*oIt);
++ oIt;
}
m_unSize = 0;
}

Object * GetObject()
{
Object * pObj = NULL;
if (0 == m_unSize)
{
pObj = new Object();
}
else
{
pObj = m_oPool.front();
m_oPool.pop_front();
-- m_unSize;
}

return pObj;
}

void ReturnObject(Object * pObj)
{
m_oPool.push_back(pObj);
++ m_unSize;
}

private:
size_t m_unSize;
std::list<object *> m_oPool;
};

不足点如下:

  1. 对象池ObjectPool只能容纳特定类型的对象,不能容纳所有类型的对象,可以支持重载的和参数不同的构造函数;
查看更多

pImpl

pImpl(Private Implementation 或 Pointer to Implementation)是通过一个私有的成员指针,将指针所指向的类的内部实现数据进行隐藏。

优点:

  1. 降低模块的耦合。因为隐藏了类的实现,被隐藏的类相当于原类不可见,对隐藏的类进行修改,不需要重新编译原类。
  2. 降低编译依赖,提高编译速度。
查看更多

RAII

什么是RAII

RAII(Resource Acquisition Is Initialization)是由 c++ 之父 Bjarne Stroustrup 提出的,中文翻译为资源获取即初始化,他说:使用局部对象来管理资源的技术称为资源获取即初始化;这里的资源主要是指操作系统中有限的东西如内存、网络套接字等等,局部对象是指存储在栈的对象,它的生命周期是由操作系统来管理的,无需人工介入;

RAII的原理

资源的使用一般经历三个步骤

  1. 获取资源
  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
template<class T>
class SharedPointer
{
public:
SharedPointer():m_refCount(nullptr), m_pointer(nullptr) {}

SharedPointer(T* adoptTarget):m_refCount(nullptr), m_pointer(adoptTarget) {
addReference();
}

SharedPointer(const SharedPointer<T>& copy):m_refCount(copy.m_refCount), m_pointer(copy.m_pointer) {
addReference();
}

virtual ~SharedPointer() {
removeReference();
}

//赋值操作
//当左值被赋值时,表明它不再指向所指的资源,故引用计数减一
//之后,它指向了新的资源,所以对应这个资源的引用计数加一
SharedPointer<T>& operator=(const SharedPointer<T>& that) {
if (this != &that) {
removeReference();
this->m_pointer = that.m_pointer;
this->m_refCount = that.m_refCount;
addReference();
}
return *this;
}


//判断是否指向同一个资源
bool operator==(const SharedPointer<T>& other) {
return m_pointer == other.m_pointer;
}
bool operator!=(const SharedPointer<T>& other) {
return !operator==(other);
}

//指针解引用
T& operator*() const {
return *m_pointer;
}
//调用所知对象的公共成员
T* operator->() const {
return m_pointer;
}

//获取引用计数个数
int GetReferenceCount() const {
if (m_refCount) {
return *m_refCount;
} else {
return -1;
}
}
protected:

//当为nullpter时,创建引用计数资源,并初始化为1
//否则,引用计数加1。
void addReference() {
if (m_refCount) {
(*m_refCount)++;
} else {
m_refCount = new int(0);
*m_refCount = 1;
}
}

void removeReference() {
if(m_refCount) {
(*m_refCount)--;
if(*m_refCount == 0) {
delete m_refCount;
delete m_pointer;
m_refCount = 0;
m_pointer = 0;
}
}
}
private:
int *m_refCount;
T *m_pointer;
};

Singleton--C++

懒汉版

最简版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Singleton {
private:
static Singleton* instance;
Singleton() {};
~Singleton() {};
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
public:
static Singleton* getInstance() {
if(instance == NULL) instance = new Singleton();
return instance;
}
};

Singleton* Singleton::instance = NULL;

存在内存泄漏的问题,解决方案:

  1. 使用智能指针
  2. 使用静态的嵌套类对象
查看更多

写一个 Move aware class

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
102
class MyString {
public:
static size_t DCtor;
static size_t Ctor;
static size_t CCtor;
static size_t CAsgn;
static size_t MCtor;
static size_t Dtor;
private:
char* _data;
size_t _len;
void _init_data(const char *s) {
_data = new char[_len + 1];
memcpy(_data, s, _len);
_data[_len] = '\0';
}
public:
// default ctor
MyString(): _data(NULL), _len(0) {
++DCtor;
}
// ctor
MyString(const char* p): _len(strlen(p)) {
++Ctor;
_init_data(p);
}

// copy ctor
MyString(const Mystring& str):_len(str._len) {
++CCtor;
_init_data(str._data);
}

// move ctor
MyString(MyString&& str) noexcept
:_data(str._data), _len(str._len) {
++MCtor;
str._len = 0;
str._data = NULL;// IMPORTANT
}

// copy assignment
MyString& operator=(const MyString& str) {
++CAsgn;
if(this != &str) { // 自我赋值检查
if(_data) delete _data;
_len = str._len;
_init_data(str._data);
} else {

}
return *this;
}

// move assignment
MyString& operator=(MyString&& str) noexcept{
++MAsgn;
if(this != &str) { // 自我赋值检查
if(_data) delete _data;
_len = str._len;
_data = str._data;
str._len = 0;
str._data = NULL;
}
return *this;
}

// dtor
virtual ~MyString() {
++Dtor;
if(_data) delete _data;
}

// operator<
bool operator<(const MyString& rhs) const {
return string(this->data) < string(rhs._data);
}
// operator==
bool operator==(const MyString& rhs) const {
return string(this->data) == string(rhs._data);
}

char* get() const { return _data; }
}


size_t MyString::DCtor = 0;
size_t MyString::Ctor = 0;
size_t MyString::CCtor = 0;
size_t MyString::CAsgn = 0;
size_t MyString::MCtor = 0;
size_t MyString::Dtor = 0;
size_t MyString::MAsgn = 0;
namespace std {
template<>
struct hash<MyString> { // for unordered containers
size_t operator() (const MyString& s) const noexcept {
return hash<string>()(string(s.get()));
// 借用现成的 hash<string>
}
}
}