c++ 程序通用多线程单例设计 c++ web 框架设计经验谈

设计 c++ web 框架时候,想要一个框架缓存类,很多通用缓存类是用字符保存,作为框架内置就不要序列和反序列了,因为框架内部使用。

想给自己的paozhu c++ web 框架添加缓存类,参考了springboot 于是确定用单例设计模式缓存类模板。

c++11后静态变量已经统一为线程安全了,网络各种茴香豆几种吃法现在变成一种安全吃法。

因为框架时候了多线程,也要求最低c++20,所以直接使用新标准单例模式。

因为需要保存多种类型,于是设计为模版接口,这样一个通用设计 缓存模型想好了,然后就是设计类库API,需要兼容数组和单一对象。

也要有超时,于是我们确定了基础结构

1
2
3
4
5
struct data_cache_t
{
    std::vector<BASE_TYPE> data;
    unsigned int exptime = 0;
};

 

因为我想以后还要动态库也能使用,于是用了一个静态函数做单例

1
2
3
4
5
6
template <typename BASETYPE_T>
std::map<std::size_t, BASETYPE_T> &get_pz_cache()
{
    static std::map<std::size_t, BASETYPE_T> instance;
    return instance;
}

 

模版类需要兼顾数组和单个对象于是统一保存为vector数组,然后套入map对象,因为我们要用size_t做hash键值,这样方便统一长度。

然后根据不同api返回不同类型。

先看详细代码,后面讲一个map插入失败情况

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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
template <typename BASE_TYPE>
   class pzcache
   {
   private:
       pzcache(){};
       ~pzcache(){};
       pzcache(const pzcache &);
       pzcache &operator=(const pzcache &);
 
   public:
       struct data_cache_t
       {
           std::vector<BASE_TYPE> data;
           unsigned int exptime = 0;
       };
 
   public:
       void save(std::size_t hashid, BASE_TYPE &data_list, int expnum = 0, bool cover_data = false)
       {
           std::map<std::size_t, data_cache_t> &obj = get_pz_cache<data_cache_t>();
           struct data_cache_t temp;
           temp.data.push_back(data_list);
           if (expnum != 0)
           {
               temp.exptime = http::timeid() + expnum;
           }
           else
           {
               temp.exptime = 0;
           }
           std::unique_lock<std::mutex> lock(editlock);
           auto [_, success] = obj.insert({hashid, temp});
           if (!success)
           {
               if (cover_data)
               {
                   obj[hashid] = temp;
               }
               else
               {
                   obj[hashid].exptime = temp.exptime;
               }
           }
       }
       void save(std::size_t hashid, std::vector<BASE_TYPE> &data_list, int expnum = 0, bool cover_data = false)
       {
           std::map<std::size_t, data_cache_t> &obj = get_pz_cache<data_cache_t>();
           struct data_cache_t temp;
           temp.data = data_list;
           if (expnum != 0)
           {
               temp.exptime = http::timeid() + expnum;
           }
           else
           {
               temp.exptime = 0;
           }
           std::unique_lock<std::mutex> lock(editlock);
           auto [_, success] = obj.insert({hashid, temp});
           if (!success)
           {
               if (cover_data)
               {
                   obj[hashid] = temp;
               }
               else
               {
                   obj[hashid].exptime = temp.exptime;
               }
           }
       }
       bool remove(std::size_t hashid)
       {
           std::map<std::size_t, data_cache_t> &obj = get_pz_cache<data_cache_t>();
           std::unique_lock<std::mutex> lock(editlock);
           auto iter = obj.find(hashid);
           if (iter != obj.end())
           {
               obj.erase(iter++);
               return true;
           }
           return false;
       }
       void remove_exptime()
       {
           std::map<std::size_t, data_cache_t> &obj = get_pz_cache<data_cache_t>();
           unsigned int nowtime = http::timeid();
           std::unique_lock<std::mutex> lock(editlock);
           for (auto iter = obj.begin(); iter != obj.end();)
           {
               if (iter->second.exptime == 0)
               {
                   continue;
               }
               if (iter->second.exptime < nowtime)
               {
                   obj.erase(iter++);
               }
           }
       }
       void clear()
       {
           std::map<std::size_t, data_cache_t> &obj = get_pz_cache<data_cache_t>();
           std::unique_lock<std::mutex> lock(editlock);
           obj.clear();
       }
       int check(std::size_t hashid)
       {
           std::map<std::size_t, data_cache_t> &obj = get_pz_cache<data_cache_t>();
           unsigned int nowtime = http::timeid();
           std::unique_lock<std::mutex> lock(editlock);
           auto iter = obj.find(hashid);
           if (iter != obj.end())
           {
               if (iter->second.exptime == 0)
               {
                   return 0;
               }
               int temp = (int)(iter->second.exptime - nowtime);
               if (temp == -1)
               {
                   return -2;
               }
               return temp;
           }
           return -1;
       }
 
       int update(std::size_t hashid, int exptime = 0)
       {
           std::map<std::size_t, data_cache_t> &obj = get_pz_cache<data_cache_t>();
           unsigned int nowtime = http::timeid() + exptime;
           if (exptime == 0)
           {
               nowtime = 0;
           }
           std::unique_lock<std::mutex> lock(editlock);
           auto iter = obj.find(hashid);
           if (iter != obj.end())
           {
               if (iter->second.exptime == 0)
               {
                   iter->second.exptime = nowtime;
                   return 0;
               }
               iter->second.exptime = nowtime;
               return 1;
           }
           return -1;
       }
       std::vector<BASE_TYPE> get_array(std::size_t hashid)
       {
           std::map<std::size_t, data_cache_t> &obj = get_pz_cache<data_cache_t>();
           unsigned int nowtime = http::timeid();
           std::unique_lock<std::mutex> lock(editlock);
           auto iter = obj.find(hashid);
           if (iter != obj.end())
           {
               if (iter->second.exptime == 0)
               {
                   return iter->second.data;
               }
 
               if (iter->second.exptime >= nowtime)
               {
                   return iter->second.data;
               }
               else
               {
                   obj.erase(iter++);
               }
           }
           lock.unlock();
           std::vector<BASE_TYPE> temp;
           return temp;
       }
       BASE_TYPE get(std::size_t hashid)
       {
           std::map<std::size_t, data_cache_t> &obj = get_pz_cache<data_cache_t>();
           unsigned int nowtime = http::timeid();
           std::unique_lock<std::mutex> lock(editlock);
           auto iter = obj.find(hashid);
           if (iter != obj.end())
           {
               if (iter->second.exptime == 0)
               {
                   if (iter->second.data.size() > 0)
                   {
                       return iter->second.data[0];
                   }
               }
 
               if (iter->second.exptime >= nowtime)
               {
                   if (iter->second.data.size() > 0)
                   {
                       return iter->second.data[0];
                   }
               }
               else
               {
                   obj.erase(iter++);
               }
           }
           lock.unlock();
           BASE_TYPE temp;
           return temp;
       }
       static pzcache &conn()
       {
           static pzcache instance;
           return instance;
       }
 
   public:
       std::mutex editlock;
   };

 

auto [_, success] = obj.insert({hashid, temp});

这个map insert 方法如果存在会插入失败,于是我用API指定是更新过期时间或删除重新添加,这一步巧妙利用了map这个特性,需要c++17以上。

 

然后使用方式就是很简单了

 

1
pzcache<std::string> &temp_cache = pzcache<std::string>::conn();

 

我们缓存一个string 对象,首先取得单例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pzcache<std::string> &temp_cache = pzcache<std::string>::conn();<br>       std::string namestring = "testname";
    std::size_t cache_hashid = std::hash<std::string>{}(namestring);
 
    if (temp_cache.check(cache_hashid) > -1)
    {
          client << "  已经存在,不需要再存 ";
    }
    else
    {
          std::string cache_data = "This cache content!";
          temp_cache.save(cache_hashid, cache_data, 30);
 
          client << "缓存新的内容";
    }

 

然后我们在其它线程使用

1
2
3
4
5
6
pzcache<std::string> &temp_cache = pzcache<std::string>::conn();
 
std::string namestring = "testname";
std::size_t cache_hashid = std::hash<std::string>{}(namestring);
 
std::string cache_data = temp_cache.get(cache_hashid);

 是不是很简单,c++ 强大的模板能力,一个通用类库设计好了,而且简单好用

 

欢迎使用 国产 C++ web 框架 paozhu 1.2.0 发布

源代码里面更多的设计模式可以参考,框架LICENSE反正为MIT模式,大家商用也没有问题。

https://github.com/hggq/paozhu

posted @   游水小鸡  阅读(802)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示