环形队列,支持数据存储和读取,自定义二分查找
1 #pragma once 2 #include <stdio.h> 3 #include <mutex> 4 #include <condition_variable> 5 #include <iostream> 6 #include <chrono> 7 #include <thread> 8 using namespace std; 9 10 #define CHECK_STATE(x) \ 11 if (x == false) \ 12 { \ 13 return false; \ 14 } \ 15 16 #pragma pack(push,1) 17 typedef struct tagCanDate 18 { 19 unsigned int m_time; 20 unsigned short m_speed; 21 unsigned int m_mileage; 22 }CanData,*LPCanData; 23 #pragma pack(pop) 24 25 typedef enum QueueProperty 26 { 27 /* if the queue is full, the new frame will override the first*/ 28 eQueueOverride, 29 /* if the queue is full, the new frame will be discarded*/ 30 eQueueDrop 31 }QueueProperty; 32 33 34 template<class _Ty = void> 35 struct compare 36 { 37 // functor for construct 38 compare(_Ty t) 39 { 40 m_data = t; 41 } 42 _Ty m_data; 43 std::function<int(_Ty, _Ty)> m_fn; 44 45 int operator()(const _Ty& obj) const 46 { 47 if (m_fn == NULL) 48 { 49 return -1; 50 } 51 return m_fn(m_data, obj); 52 } 53 }; 54 55 template<class T> 56 class TQueue 57 { 58 public: 59 TQueue(); 60 ~TQueue() = default; 61 public: 62 bool Initial(unsigned int size, QueueProperty property = eQueueOverride); 63 void Destroy(); 64 bool WriteData(T data); 65 bool ReadData(T &data); 66 bool Reset(); 67 bool Pop(unsigned int count = 1); 68 bool IsFull(); 69 bool IsEmpty(); 70 unsigned int GetQueueUsedSize(); 71 unsigned int GetQueueSize(); 72 const T* operator[](unsigned int index) const; 73 74 //get the first frame 75 const T* head() const; 76 77 //get the tail frame 78 const T* tail() const; 79 80 bool SaveFile(string path); 81 82 bool LoadFile(string path); 83 84 template<class _Pr = compare<T> > 85 const T* BinarySearch(_Pr _Pred) 86 { 87 CHECK_STATE(m_state); 88 std::unique_lock<Mutex> lock(m_mutex); 89 unsigned int middle = 0, low = 0, high = m_used -1; 90 while (low < high) 91 { 92 middle = (low + high) / 2; 93 T *data = &m_frameQ[(m_head + middle) % m_size]; 94 int ret = _Pred(*data); 95 if (ret == 0){ 96 return data; 97 } 98 else if (ret > 0){ 99 high = middle; 100 } 101 else{ 102 low = middle + 1; 103 } 104 } 105 return NULL; 106 } 107 private: 108 /* std::mutex is not reentrant, the exception is thrown if we are trying to 109 lock mutex while the mutex is already owned by calling thread*/ 110 typedef std::recursive_mutex Mutex; 111 bool m_state; 112 unsigned int m_tail; 113 unsigned int m_head; 114 unsigned int m_size; 115 unsigned int m_used; 116 QueueProperty m_property; 117 Mutex m_mutex; 118 T* m_frameQ; 119 }; 120 121 template<class T> 122 TQueue<T>::TQueue() 123 { 124 m_size = 0; 125 m_head = 0; 126 m_tail = 0; 127 m_used = 0; 128 m_state = false; 129 m_frameQ = NULL; 130 } 131 132 133 template<class T> 134 const T* TQueue<T>::operator[](unsigned int index) const 135 { 136 if (index >= m_used) 137 { 138 return NULL; 139 } 140 return &(m_frameQ[(m_head + index) % m_size]); 141 } 142 143 //get the first frame 144 template<class T> 145 const T* TQueue<T>::head() const 146 { 147 if (m_used == 0) 148 { 149 return NULL; 150 } 151 return &(m_frameQ[m_head % m_size]); 152 } 153 154 //get the tail frame 155 template<class T> 156 const T* TQueue<T>::tail() const 157 { 158 if (m_used == 0) 159 { 160 return NULL; 161 } 162 return &(m_frameQ[(m_head + m_used - 1) % m_size]); 163 } 164 165 template<class T> 166 bool TQueue<T>::Initial(unsigned int size, QueueProperty property) 167 { 168 CHECK_STATE(!m_state); 169 if (size == 0) 170 { 171 return false; 172 } 173 m_size = size; 174 m_frameQ = new T[m_size]; 175 if (m_frameQ == NULL) 176 { 177 return false; 178 } 179 m_state = true; 180 Reset(); 181 m_property = property; 182 return true; 183 } 184 185 template<class T> 186 void TQueue<T>::Destroy() 187 { 188 std::unique_lock<Mutex> lock(m_mutex); 189 Reset(); 190 m_state = false; 191 if (!m_frameQ) 192 { 193 delete[] m_frameQ; 194 m_frameQ = NULL; 195 } 196 } 197 198 template<class T> 199 bool TQueue<T>::WriteData(T data) 200 { 201 CHECK_STATE(m_state); 202 std::unique_lock<Mutex> lock(m_mutex); 203 if (IsFull()) 204 { 205 if (m_property == eQueueOverride) 206 { 207 //the incoming frame will override the first if the queue is full 208 m_head = (m_head + 1) % m_size; 209 --m_used; 210 } 211 else 212 { 213 Sleep(1); 214 return false; 215 } 216 } 217 m_frameQ[m_tail] = data; 218 ++m_used; 219 m_tail = (m_tail + 1) % m_size; 220 return true; 221 } 222 223 template<class T> 224 bool TQueue<T>::ReadData(T &data) 225 { 226 CHECK_STATE(m_state); 227 std::unique_lock<Mutex> lock(m_mutex); 228 if (IsEmpty()) 229 { 230 //it will return if the queue is empty 231 Sleep(1); 232 return false; 233 } 234 data = m_frameQ[m_head]; 235 --m_used; 236 m_head = (m_head + 1) % m_size; 237 return true; 238 } 239 240 template<class T> 241 bool TQueue<T>::Reset() 242 { 243 try 244 { 245 CHECK_STATE(m_state); 246 std::unique_lock<Mutex> lock(m_mutex); 247 if (m_frameQ != NULL) 248 { 249 memset(m_frameQ, 0, m_size * sizeof(T)); 250 } 251 m_head = 0; 252 m_tail = 0; 253 m_used = 0; 254 } 255 catch (const std::exception &e) 256 { 257 std::cout << e.what() << '\n'; 258 throw e; 259 } 260 return true; 261 } 262 263 264 template<class T> 265 bool TQueue<T>::Pop(unsigned int count) 266 { 267 CHECK_STATE(m_state); 268 std::unique_lock<Mutex> lock(m_mutex); 269 //calculate how many frame to drop 270 unsigned int popCount = m_used > count ? count : m_used; 271 if (popCount < 1) 272 { 273 //the function returns if the count of pop frames is less than 1 274 Sleep(0); 275 return false; 276 } 277 m_used -= popCount; 278 m_head = (m_head + popCount) % m_size; 279 return true; 280 } 281 282 template<class T> 283 unsigned int TQueue<T>::GetQueueUsedSize() 284 { 285 return m_used; 286 } 287 288 template<class T> 289 unsigned int TQueue<T>::GetQueueSize() 290 { 291 return m_size; 292 } 293 294 template<class T> 295 bool TQueue<T>::IsFull() 296 { 297 return (m_used == m_size); 298 } 299 300 template<class T> 301 bool TQueue<T>::IsEmpty() 302 { 303 return (m_used == 0); 304 } 305 306 template<class T> 307 bool TQueue<T>::SaveFile(string path) 308 { 309 CHECK_STATE(m_state); 310 std::unique_lock<Mutex> lock(m_mutex); 311 if (path.length() == 0) 312 { 313 return false; 314 } 315 bool ret = true; 316 FILE * fd = NULL; 317 fopen_s(&fd, path.c_str(), "wb"); 318 if (NULL != fd) 319 { 320 int total_len = 0; 321 int len = 0; 322 int offset = 0; 323 int data_len = sizeof(T); 324 int try_count = 0; 325 for (int i = 0; i < m_used; i++) 326 { 327 const T data = m_frameQ[(m_head + i) % m_size]; 328 while (offset != data_len) 329 { 330 len = fwrite(&data + offset, 1, data_len - offset, fd); 331 if (len == -1) 332 { 333 if (++try_count > 3) 334 { 335 ret = false; 336 break; 337 } 338 else 339 { 340 printf("SaveFile fwrite err:%d\n", errno); 341 Sleep(0); 342 continue; 343 } 344 } 345 offset += len; 346 total_len += len; 347 } 348 try_count = 0; 349 len = 0; 350 offset = 0; 351 } 352 fflush(fd); 353 fclose(fd); 354 } 355 else 356 { 357 ret = false; 358 printf("SaveFile fopen err:%d\n", errno); 359 } 360 return ret; 361 } 362 363 template<class T > 364 bool TQueue<T>::LoadFile(string path) 365 { 366 CHECK_STATE(m_state); 367 std::unique_lock<Mutex> lock(m_mutex); 368 if (path.length() == 0) 369 { 370 return false; 371 } 372 bool ret = true; 373 struct stat st; 374 if (::stat(path.c_str(), &st) != 0) 375 { 376 printf("LoadFile file is not exist!\n"); 377 return false; 378 } 379 _off_t file_size = st.st_size; 380 FILE * fd = NULL; 381 fopen_s(&fd, path.c_str(), "rb+"); 382 if (NULL != fd) 383 { 384 int total_len = 0; 385 int len = 0; 386 int offset = 0; 387 int try_count = 0; 388 int data_len = sizeof(T); 389 T data; 390 Reset(); 391 for (; file_size >= total_len + data_len; ) 392 { 393 memset(&data, 0, data_len); 394 while (offset != data_len) 395 { 396 len = fread(&data + offset, 1, data_len - offset, fd); 397 if (len == -1) 398 { 399 if (++try_count > 3) 400 { 401 ret = false; 402 break; 403 } 404 else 405 { 406 printf("LoadFile fread err:%d\n", errno); 407 Sleep(0); 408 continue; 409 } 410 } 411 offset += len; 412 total_len += len; 413 if (feof(fd) && offset != data_len) 414 { 415 //the function is return when the end indicator of file is set and offset is not equal data_len 416 printf("LoadFile file is end!\n"); 417 return ret; 418 } 419 } 420 WriteData(data); 421 try_count = 0; 422 len = 0; 423 offset = 0; 424 } 425 fclose(fd); 426 } 427 else 428 { 429 ret = false; 430 } 431 return ret; 432 }
测试代码
1 int main() 2 { 3 time_t cur = time(NULL); 4 auto fn_time = [](unsigned int t)->string { 5 char buff[128] = { '\0' }; 6 time_t now = t; 7 struct tm cur; 8 localtime_s(&cur, &now); 9 sprintf_s(buff, sizeof(buff), "%04d-%02d-%02d %02d:%02d:%02d", cur.tm_year + 1900, cur.tm_mon + 1, cur.tm_mday, cur.tm_hour, cur.tm_min, cur.tm_sec); 10 return buff; 11 }; 12 TQueue<CanData> CanQueue; 13 //3*24*60*60/10 14 CanQueue.Initial(25920); 15 //CanQueue.Initial(10); 16 auto fn_write = [cur](TQueue<CanData> &CanQueue) { 17 printf("fn_write CanQueue address:0x%08x \n", &CanQueue); 18 short index = 0; 19 do 20 { 21 CanData data; 22 data.m_mileage = index; 23 data.m_speed = index; 24 data.m_time = (unsigned int)cur + index * 10; 25 CanQueue.WriteData(data); 26 //printf("write data , mileage:%d, speed:%d, time:%d \n", data.mileage, data.speed, data.time); 27 index++; 28 if (CanQueue.IsFull()) 29 { 30 CanQueue.SaveFile("raw.txt"); 31 break; 32 } 33 //Sleep(50); 34 } while (1); 35 }; 36 std::thread QueueWrite(fn_write, std::ref(CanQueue)); 37 QueueWrite.join(); 38 auto fn_read = [cur, fn_time](TQueue<CanData> *CanQueue) { 39 printf("fn_read CanQueue address:0x%08x \n", CanQueue); 40 do 41 { 42 /*CanData data; 43 const CanData *pData = (*CanQueue)[9]; 44 CanQueue->ReadData(data); 45 int result = memcmp(pData, &data, sizeof(pData)); 46 printf("read data ,result:%d,count:%d, mileage:%d, speed:%d, time:%d \n", result,CanQueue->GetQueueUsedSize(), data.mileage, data.speed, data.time); 47 */ 48 if (CanQueue->IsFull()) 49 { 50 auto t1 = std::chrono::high_resolution_clock::now(); 51 CanQueue->LoadFile("raw.txt"); 52 auto t2 = std::chrono::high_resolution_clock::now(); 53 printf("it cost :%lldms to read data!\n", std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1)); 54 } 55 { 56 //二分法进行数据查找 57 auto t1 = std::chrono::high_resolution_clock::now(); 58 CanData canData; 59 canData.m_time = (unsigned int)cur + 6000 * 10; 60 //自定义数据比较方法 61 struct compare<CanData> cmp(canData); 62 cmp.m_fn = [](const CanData &first, const CanData &second)->int { 63 if (first.m_time + 10 >= second.m_time && first.m_time <= second.m_time) { 64 return 0; 65 } 66 else if (first.m_time + 10 < second.m_time) { 67 return 1; 68 } 69 else if (first.m_time > second.m_time) { 70 return -1; 71 } 72 }; 73 //查找 74 const CanData *pCanData = CanQueue->BinarySearch(cmp); 75 if (pCanData != NULL) 76 { 77 string t1 = fn_time(pCanData->m_time); 78 string t2 = fn_time(canData.m_time); 79 printf("serach time:%s, base time:%s\n", t1.c_str(), t2.c_str()); 80 } 81 auto t2 = std::chrono::high_resolution_clock::now(); 82 printf("it cost :%lldms to read data!\n", std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1)); 83 } 84 break; 85 } while (1); 86 }; 87 std::thread QueueRead(fn_read, &CanQueue); 88 QueueRead.join(); 89 system("Pause"); 90 }