内存中的树实例同步持久化的实现

本文介绍了一种使用C#实现树结构数据在内存中维护及同步持久化至MySQL数据库的方法。通过构建树节点类及其关系,文章详细阐述了树的加载、操作和保存流程,并确保线程安全。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在开发中,可能会有这样的需求: 需要在内存中维护一颗树,并且该树可以同步的持久化。

此文给出一种简单的解决思路。使用C#作为范例。

一般的持久化无非是写文件 或者数据库,本文采用MYSQL数据库。

对于每个节点,记录其父节点id。

表结构如下:

int id 主键

int parentid not null

XXXX 其他数据

内存中数据结构如下:

class Node{

Node parent;

List<Node> children;

XXXX 其他数据

}

所以最主要考虑的问题就是对树的应用以及其导致的持久化读写的时机。

对于应用程序来说,数据库层对其应是透明的。就是应用开发的程序员只需要使用树提供的相关接口,而不必理解其如何被持久。结构如下:

那么我们的程序在启动的时候就应该读入整颗树,以保证其是对数据库的映射。

每次对树的操作(通过树相关API),都应该修改树节点并同步持久(如何持久?这里涉及到效率问题。)

同时,我们的树应该还是支持多线程访问,那么多线程的情景下,持久又要如何进行?如何保证线程安全?

在我的实现中,树实例是由TreeNode链来维护的,没有一个诸如 Tree 的对象,而根节点维护在TreeManager中。

很多情况下,我们的“树实例”是一个树的集合(森林),而不是单独的一棵树,所以在TreeManager中我设置了一个虚拟的根节点,令所有树的根节点均作为虚拟根节点的叶子,统一管理整个森林。这很好理解。

由于树实例是唯一维护,所以我的TreeManager选择了singleton模式。(注意这里使用到安全的多线程singleton构建方法)

接下来就是对持久过程加互斥锁。

持久分为整体持久和部分持久,应该提供不同的方法(如修改一个叶子不用持久整棵树,而只需要持久该叶子节点即可),下文给出简单的整树持久和叶子持久的示例。

对于整颗树的持久过程,应该使用数据库事务,否则若在其中某叶子失败,而导致整个持久化数据混乱。

下面给出TreeManager主要代码,DB层的无非就是读库写库,此处不再赘述:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using NetVideo.TaskTrace.Model; using NetVideo.TaskTrace.DAL; using NetVideo.TaskTrace.Common.DBHelper; namespace NetVideo.TaskTrace.BLL { /// <summary> /// 任务树管理器 /// </summary> public class TreeManager { #region singleton static private TreeManager _instance = null; static private Object _mutex = new Object(); static public TreeManager getInstance() { if( _instance == null) { lock(_mutex) { if(_instance == null ) _instance = new TreeManager(); } } return _instance; } private TreeManager() { virtualRoot = new TaskTreeNode(0, "虚拟根节点", 0, 0, true); } #endregion #region 属性 private TaskTreeNode virtualRoot = null; //虚拟根节点 #endregion #region 公有方法 public TaskTreeNode getVirtualRoot() { return virtualRoot; } public void AddRootTree(TaskTreeNode root) { virtualRoot.Children.Add(root); root.Parent = virtualRoot; } /// <summary> /// 从任务树中获取节点 /// </summary> /// <param name="id"></param> /// <returns></returns> public TaskTreeNode getNodeById(int id) { //广度优先搜索 List<TaskTreeNode> todoList = new List<TaskTreeNode>(); todoList.Add(virtualRoot); while (todoList.Count>0) { TaskTreeNode currNode = todoList[0]; if (currNode.ID == id) return currNode; else { todoList.AddRange(currNode.Children); todoList.Remove(currNode); } } return null; } /// <summary> /// 增加孩子 /// </summary> /// <param name="parentId">父节点id</param> /// <param name="son">孩子节点</param> /// <returns>是否添加成功</returns> public bool AddSon(int parentId, TaskTreeNode son) { TaskTreeNode parent = this.getNodeById(parentId); return this.AddSon(parent, son); } /// <summary> /// 增加孩子 /// </summary> /// <param name="parent">父节点</param> /// <param name="son">孩子节点</param> /// <returns>是否添加成功</returns> public bool AddSon(TaskTreeNode parent, TaskTreeNode son) { if( parent == null || son == null || parent == virtualRoot || parent.Children.Contains(son)) return false; parent.Children.Add(son); son.Parent = parent; this.UpdateNode(parent); //写持久层 this.UpdateNode(son); return true; } /// <summary> /// 删除节点(不会写入持久层) /// </summary> /// <param name="id"></param> /// <returns></returns> public bool deleteNodeById(int id) { return this.deleteNode(this.getNodeById(id)); } /// <summary> /// 删除节点(不会写入持久层) /// </summary> /// <param name="node"></param> /// <returns></returns> public bool deleteNode(TaskTreeNode node) { if (node==null||node == virtualRoot||node.Parent == null) return false; return (node.Parent.Children.Remove(node)); } static Object _dalMutex = new object(); /// <summary> /// 从持久层载入任务树 /// </summary> /// <returns></returns> public bool Load() { lock (_dalMutex) { try { virtualRoot = new TaskTreeNode(0, "虚拟根节点", 0, 0, true);//新建虚拟根节点 List<TaskTreeNode> nodes = DataAccess.CreateTaskNode().getAll();//载入任务 //生成任务树 foreach (TaskTreeNode node in nodes) { if (node.ParentId == 0) //根任务 { TreeManager.getInstance().AddRootTree(node); //设置成虚拟根节点孩子 } foreach (TaskTreeNode obj in nodes)//寻找该节点的孩子 { if (obj.ParentId == node.ID) { obj.Parent = node; if (!node.Children.Contains(obj)) node.Children.Add(obj); } } } return true; } catch (Exception e) { return false; } } } /// <summary> /// 向持久层写入任务树 /// 注:游离节点将不会被写入 /// </summary> /// <returns></returns> public bool Save() { lock (_dalMutex) { MysqlTransactionHelper db = new MysqlTransactionHelper(); db.BeginTransaction();//事务启动 try { //广度优先,按层遍历节点 List<TaskTreeNode> todoList = new List<TaskTreeNode>(); todoList.Add(virtualRoot); while (todoList.Count > 0) { TaskTreeNode currNode = todoList[0]; if (currNode != virtualRoot) //不写入根节点 { if (DataAccess.CreateTaskNode().insertOrUpdate(currNode, db) == false) //事务失败 { return false; } } todoList.AddRange(currNode.Children); todoList.Remove(currNode); } db.Commit(); return true; } catch (Exception e) { return false; } } } /// <summary> /// 向持久层更新节点 /// </summary> /// <param name="node"></param> /// <returns>是否成功</returns> public bool UpdateNode(TaskTreeNode node) { lock (_dalMutex) { return DataAccess.CreateTaskNode().insertOrUpdate(node); } } #endregion } }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值