最近遇到这么一个问题,一个处理路径的函数,在多线程中被调用,我需要记录某个状态的路径,并在后续当再次处于某个状态时,从链表中将其取出。
为了满足这个需求,用来存储的数据结构要满足:
作为一个“资深”程序员,我已经熟练掌握了从浩如烟海的百度中用CV大法来进行开发的武功。
所以,我很快地搜索到一段代码:
#include
#include
#include
template // 模板
class ThreadsafeList
{
struct node // 定义链表节点
{
std::mutex m; // 互斥体,用来保证线程安全,锁住节点
std::shared_ptr data; // 数据,不必多讲
std::unique_ptr next; // 指向下一个节点
node() : next(){}
node(T const& value) : data(std::make_shared(value)) // 拷贝构造函数
{}
};
node head;
public:
ThreadsafeList()
{}
~ThreadsafeList()
{
remove_if([](T const&) {return true; });
}
ThreadsafeList(ThreadsafeList const& other) = delete;
ThreadsafeList& operator=(ThreadsafeList const& other) = delete;
void push_front(T const& value) // 在头节点插入函数
{
std::unique_ptr new_node(new node(value)); // 创建一个新的节点
std::lock_guard lk(head.m); // 锁
new_node->next = std::move(head.next);
head.next = std::move(new_node);
}
template
void for_each(Function f) // 遍历函数
{
node* current = &head;
std::unique_lock lk(head.m); // 对头节点上锁
while (node* const next = current->next.get())
{
std::unique_lock next_lk(next->m); // 上锁
lk.unlock(); // 对上个节点解锁
f(*next->data);
current = next;
lk = std::move(next_lk);
}
}
template
std::shared_ptr find_first_if(Predicate p) // 查询函数
{
node* current = &head;
std::unique_lock lk(head.m);
while (node* const next = current->next.get())
{
std::unique_lock next_lk(next->m);
lk.unlock();
if (p(*next->data))
{
return next->data;
}
current = next;
lk = std::move(next_lk);
}
return std::shared_ptr();
}
template
void remove_if(Predicate p) // 删除函数
{
node* current = &head;
std::unique_lock lk(head.m);
while (node* const next = current->next.get())
{
std::unique_lock next_lk(next->m);
if (p(*next->data))
{
std::unique_ptr old_next = std::move(current->next);
current->next = std::move(next->next);
next_lk.unlock();
}
else
{
lk.unlock();
current = next;
lk = std::move(next_lk);
}
}
}
};
int main()
{
//简单函数功能测试
ThreadsafeList list;
list.push_front(1);
list.for_each([](const int& item) {
std::cout << item << " ";
});
std::cout << std::endl;
std::shared_ptr ptr=list.find_first_if([](const int& item) {return item == 1; });
if (ptr.get()!=nullptr) {
std::cout << *ptr << std::endl;
}
}
在类ThreadSafeList中直接声明了一个结构体变量,比较少见,我也顺手查了一下。
这种方法和在面定义结构体,没什么区别,但是这里需要主义的是,在引用node节点的时候,没有public、private等,所以他是私有的。
class中默认的成员访问权限是private的,而struct中则是public的。
结构体中定义了一个互斥体,这就是保障线程安全的关键所在,每次要对节点进行操作的时候,就先上锁。
struct node // 定义链表节点
{
std::mutex m; // 互斥体,用来保证线程安全,锁住节点
std::shared_ptr<T> data; // 数据,不必多讲
std::unique_ptr<node> next; // 指向下一个节点
node() : next(){}
node(T const& value) : data(std::make_shared<T>(value)) // 拷贝构造函数
{}
};
以查询函数为例:
std::unique_lockstd::mutex next_lk(next->m); 这是上锁操作,且使用的是unique_lock,这样就有俩种解锁方式,一种是unlock(),第二种就是在析构函数中解锁了,这也防止我们忘记解锁,这也满足了RAII原则。
template<typename Predicate>
std::shared_ptr<T> find_first_if(Predicate p) // 查询函数
{
node* current = &head;
std::unique_lock<std::mutex> lk(head.m);
while (node* const next = current->next.get())
{
std::unique_lock<std::mutex> next_lk(next->m);
lk.unlock();
if (p(*next->data))
{
return next->data;
}
current = next;
lk = std::move(next_lk);
}
return std::shared_ptr<T>();
}
综上所述,这个类是满足我们选择“线程安全链表”的需求的
写一个例子来测试一下。
这是我模拟我的需求写的一个简易例子,多线程对类中同一个数据链表进行读写访问,经过检测,没有出现问题!
// ThreadSafe.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include
#include
#include "thread_safe_list.hpp"
class ThreadSafeListTest
{
public:
ThreadSafeListTest();
~ThreadSafeListTest();
void thread_safe_list_test1();
void thread_safe_list_test2();
void thread_safe_list_test3();
private:
ThreadsafeList<std::string> _name_list;
};
ThreadSafeListTest::ThreadSafeListTest()
{
}
ThreadSafeListTest::~ThreadSafeListTest()
{
}
void ThreadSafeListTest::thread_safe_list_test1()
{
std::string target_name = "james";
_name_list.push_front("harden");
auto name = _name_list.find_first_if([target_name](const std::string& name) {return target_name == name; });
if (name != nullptr)
{
_name_list.remove_if([target_name](const std::string& name) {return target_name == name; });
}
}
void ThreadSafeListTest::thread_safe_list_test2()
{
std::string target_name = "james";
_name_list.push_front("curry");
auto name = _name_list.find_first_if([target_name](const std::string& name) {return target_name == name; });
if (name != nullptr)
{
_name_list.remove_if([target_name](const std::string& name) {return target_name == name; });
}
}
void ThreadSafeListTest::thread_safe_list_test3()
{
std::string target_name = "Fakejamesjames";
_name_list.push_front("james");
auto name = _name_list.find_first_if([target_name](const std::string& name) {return target_name == name; });
if (name != nullptr)
{
_name_list.remove_if([target_name](const std::string& name) {return target_name == name; });
}
}
void test1(ThreadSafeListTest& tslt)
{
while (true)
{
tslt.thread_safe_list_test1();
}
}
void test2(ThreadSafeListTest& tslt)
{
while (true)
{
tslt.thread_safe_list_test2();
}
}
void test3(ThreadSafeListTest& tslt)
{
while (true)
{
tslt.thread_safe_list_test3();
}
}
int main()
{
ThreadSafeListTest test;
std::thread t1(test1, std::ref(test));
std::thread t2(test2, std::ref(test));
std::thread t3(test3, std::ref(test));
t1.join(); // join和主线程不分离,一起结束
t2.join();
t3.join();
system("pause");
return 0;
}