简介
最近实现了一个线程安全的双向链表,使用的是C++类的模式写的,和以前学习数据结构使用struct结构体实现的方式不同,对函数进行了封装,以及使用了C++中的类模板,还有unique_lock锁,对内存泄漏也进行了优化。
如果想继续学习各种锁的使用,请关注我,我后面会陆续更新各种锁的使用。如有错误的地方欢迎评论区指出。
以下是代码:(注意:请勿在业务代码中使用,代码未经过完全的测试)
内存泄漏的思考
请看如下代码的对比,上面的存在内存泄漏,下面的不存在内存泄漏。
原因是:上面的代码执行后,中间结点的内存是没有释放的(由于RAII,内存会在程序结束的时候进行释放),如果有指针指向了其中的结点,程序依然可以访问到结点的值,这显然是我们不想看到的。
在改进后,下面的代码对不要的结点都进行了显示的内存释放,这样就不存在内存泄漏了。
/*
// 这里存在内存泄漏
template<typename T>
void DoubleList<T>::Clear(){
unique_lock<mutex> mtx (mtx_list);
head->next = tail;
tail->pre = head;
length = 0;
}
*/
template<typename T>
void DoubleList<T>::Clear(){
unique_lock<mutex> mtx (mtx_list);
auto p = head->next;
while(p != tail){
head->next = p->next;
delete p;
p = head->next;
}
length = 0;
}
代码实现
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
template <typename T>
class ListNode{
public:
T val;
ListNode* next;
ListNode* pre;
};
template<typename T>
class DoubleList{
public:
DoubleList(){};
~DoubleList(){};
void InitList();
bool Insert(int pos,T data);
bool Erase(int pos);
void Clear();
ListNode<T>* Find(T target);
void Print();
int getlength();
private:
mutex mtx_list;
ListNode<T>* head;
ListNode<T>* tail;
int length;
};
template<typename T>
int DoubleList<T>::getlength(){
return length;
}
template<typename T>
void DoubleList<T>::InitList(){
unique_lock<mutex> mtx (mtx_list);
head = new ListNode<T>();
tail = new ListNode<T>();
head->next = tail;
head->pre = nullptr;
tail->next = nullptr;
tail->pre = head;
length = 0;
}
template<typename T>
bool DoubleList<T>::Insert(int pos,T data){
unique_lock<mutex> mtx (mtx_list);
if(pos > length || pos < 0){
return false;
}
ListNode<T>* pre = head, *p = head->next;
ListNode<T>* temp = new ListNode<T>();
temp->val = data;
while(pos){
pre = p;
p = p->next;
--pos;
}
temp -> next = p;
pre->next = temp;
temp->pre = p->pre;
p->pre = temp;
temp = nullptr;
++length;
return true;
}
template<typename T>
bool DoubleList<T>::Erase(int pos){
if(pos < 0){
return false;
}
unique_lock<mutex> mtx (mtx_list);
if(pos > length || length == 0){
return false;
}else{
ListNode<T>* pre = head, *p = head->next;
while(pos > 0){
pre = p;
p = p->next;
--pos;
}
pre->next = p->next;
p->next->pre = p->pre;
delete p;
--length;
return true;
}
}
/*
// 这里存在内存泄漏
template<typename T>
void DoubleList<T>::Clear(){
unique_lock<mutex> mtx (mtx_list);
head->next = tail;
tail->pre = head;
length = 0;
}
*/
template<typename T>
void DoubleList<T>::Clear(){
unique_lock<mutex> mtx (mtx_list);
auto p = head->next;
while(p != tail){
head->next = p->next;
delete p;
p = head->next;
}
length = 0;
}
template<typename T>
ListNode<T>* DoubleList<T>::Find(T target){
unique_lock<mutex> mtx (mtx_list);
ListNode<T>* p = head->next;
while(p != tail){
if(p->val == target){
return p;
}
p = p->next;
}
return nullptr;
}
template<typename T>
void DoubleList<T>::Print(){
unique_lock<mutex> mtx(mtx_list);
ListNode<T>* p = head->next;
if (length == 0)
{
std::cout << "List is empty" << std::endl;
return;
}
while(p != tail){
std::cout << p->val << " ";
p = p->next;
}
cout << endl;
}
int main(){
DoubleList<int> L;
L.InitList();
// 写
thread t1 ([&L](){
for(int i=0; i < 5000;++i){
L.Insert(0,1);
}
});
thread t2 ([&L](){
for(int i=0; i < 5000;++i){
L.Insert(0,2);
}
});
t1.join();
t2.join();
L.Print();
if(L.getlength() == 10000){
cout << "success" <<endl;
}else{
cout << "fail" << endl;
}
cout << L.getlength()<<endl;
// 查找
thread t3 ([&L](){
for(int i=4000; i < 6000; ++i){
auto p = L.Find(i);
}
});
thread t4 ([&L](){
for(int i=2000; i < 3000; ++i){
auto p = L.Find(i);
}
});
t3.join();
t4.join();
//删除
thread t5 ([&L](){
for(int i=4000; i < 6000; ++i){
auto p = L.Erase(i);
}
});
thread t6 ([&L](){
for(int i=2000; i < 3000; ++i){
auto p = L.Erase(i);
}
});
t5.join();
t6.join();
cout << L.getlength() <<endl;
}