性能优化总结(四):预加载的设计

    本节说一下数据的预加载。这节的内容与SQL没什么关系。主要说的是在 GIX4项目 中,我们是如何设计符合需求的预加载类库的。内容如下:

  1. 什么是预加载,为什么要用它?
  2. 我们所需要的API
  3. 一个简单的例子

什么是预加载?

    预加载其实就是在真正开始使用数据之前,异步把数据加载好,等到需要使用时,就可以直接使用之前加载好的数据。这时,由于数据已经加载完成,而不用等待漫长的加载过程,所以程序的速度得到一个明显的提升。

    那么,什么时候需要使用它呢?我觉得,主要是这种情况:当我们可以预知程序接下来的步骤中,很可能会用到一些数据,而获取这些数据的操作比较耗时的时候,我们就可以使用预加载的方式,提前把数据准备好

    预加载需要使用异步方法,也就是使用后台线程来加载数据。这样做的好处当然是不会阻塞当前的主线程。(不过如果当前线程本身就是用于异步加载数据的话,那就没必要再新开线程了。)

    我们可以使用很多种方式来实现异步加载:在.NET Framework的类库中,很多地方都提供了异步编程模式(Asynchronous Programming Design Patterns)的API,使用这个模式,可以方便地实现各种异步加载。当然,我们也可以使用2.0提供的ThreadPool.QueueUserWorkItem来实现一些轻量级的异步操作。在.NET4.0最新的API中,提供了Task类来表示可执行任务。

    但是,这些并不是我想要的API……

我们所需要的API

    目前系统中预加载使用的场景需求是这样的:

  1. 预加载可以对指定的数据获取操作(loading action)进行封装,在需要时调用。
  2. 使用数据的模块(使用者),并不一定知道是谁、在何时给它提前加载的数据。它只会申请使用数据。
  3. 发起异步加载的模块(发起者),应该知道使用者是谁。
  4. 多个发起者之间没有关系,但是都可以为某一使用者发起预加载。但是保证真正的数据加载操作,只会发生一次。
  5. 支持重新加载。
  6. 一个类中,支持对它不同的数据进行不同加载方式,以方便按需加载。
  7. 从使用者的角度来看,不管有没有发起者为它进行预加载,它都可以申请并拿到想要的数据。也就是说: 当没有发起者为它进行预加载,那么它的数据申请会导致即时的数据加载; 如果已经发起了预加载,而且数据已经加载完成,则直接获取到加载好的数据; 如果数据没有完成,则数据使用者需要等待数据的加载完成后,才可以获取到数据并继续当前的操作。

    其中,最重要的就是最后点。

    可以看到,这里需要用到异步操作、线程间同步。所以我们需要基于上面提到的多种API来实现,这里我们使用的是简单的线程池的方式,比较简单,不再赘述。

    最后设计出的API大致是这样的:

namespace OpenExpressApp
{
    public enum LoaderStatus
    {
        NotStarted = 0,
        Running = 1,
        Completed = 2,
        Failed = 3,
    }

    public class ForeAsyncLoader
    {
        public ForeAsyncLoader(Action loadAction);

        public LoaderStatus Status { get; }

        public event EventHandler ActionSucceeded;

        /// <summary>
        /// 申请启用线程进行预加载。
         /// 注意:
         /// 本方法可以重入,多次调用也只会执行一次ladAction
         /// </summary>
        public void BeginLoading();
        /// <summary>
        /// 重设加载器。
         /// 使用此方法后,再次申请预加载时,会再次执行loadAction。
        /// </summary>
        public void Reset();
        /// <summary>
        /// 等待数据加载完成。
         /// </summary>
        public void WaitForLoading();
    }
}
例子
    客户程序使用时,需要为其定义一个属性,举例如下:
数据持有者:
public class DataHolder
{
    private object _data;

    public object Data
    {
        get
        {
            return this._data;
        }
    }

    private object GetDataFromWeb()
    {
        //...
    }
}
如果它的data1数据加载比较慢,我们可以为其定义一个预加载属性:
private ForeAsyncLoader _dataLoader;

/// <summary>
/// 数据加载器
/// </summary>
public ForeAsyncLoader DataLoader
{
    get
    {
        if (this._dataLoader == null)
        {
            this._dataLoader = new ForeAsyncLoader(() =>
            {
                //真正加载数据
                this._data = this.GetDataFromWeb();
            });
        }
        return this._dataLoader;
    }
}
这样,数据的“消费者”就可以使用这个数据:
public class DataConsumer
{
    private void Process(DataHolder holder)
    {
        holder.DataLoader.WaitForLoading();
        var data = holder.Data;
        //consume data...
    }
}
在这里,虽然使用者并不知道有没有其它代码给holder执行了数据的预加载,但是当WaitForLoading方法执行完成后,数据是必然获取到本地了。所以就可以直接使用数据。我们甚至可以把这句代码放在Data属性的get代码块中,这样,使用者甚至都不知道数据的获取方案!
然后,可以在运行于它之前的代码中,为这个“DataHolder”申请预加载。例如,我们在应用程序启动的时候,就开始预加载。下面的方法调用了BeginLoading方法,此方法会使用后台线程加载数据,所以这里会立即返回:

public class Invoker
{
    private DataHolder _holder = new DataHolder();

    private void App_Start()
    {
        this._holder.DataLoader.BeginLoading();
        //do other things
    }
}

至此,就完成了一个最简单的预加载。

过程如下:

(图画得不熟,哪画错了,望大家指正,谢谢。)

小结

本篇主要说了一下在目前的系统中,如何设计出一个满足场景应用需求的预加载API。

预加载是一个经常会被使用到的模式,希望对大家有用。

下一篇我会写一下与目前系统关联比较大的内容:与GIX4对象模型相关的“预加载、延迟加载、聚合SQL的组合应用”;另外可能会顺便说一下,如何让CSLA的服务端框架支持多线程并发。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏bboysoul

编译安装squid

什么是squid,简单的说squid是一款代理缓存软件就是加速网站访问的,国内部分cdn服务用的就是squid,cdn大家应该清楚吧,内容分发网络,cdn的作用...

21530
来自专栏FreeBuf

北极熊扫描器3.5发布:增加超级搜索等功能

文中提及的部分技术、工具可能带有一定攻击性,仅供安全学习和教学用途,禁止非法使用! ? 软件介绍 本软件是一款为白帽子提供网站安全检测的软件,软件完全免费,您可...

37550
来自专栏一个爱瞎折腾的程序猿

weexpack打包weex项目运行/打包记录

weexpack 是 weex 新一代的工程开发套件,是基于weex快速搭建应用原型的利器。它能够帮助开发者通过命令行创建weex工程,添加相应平台的weex ...

24120
来自专栏小白安全

Freebuf漏斗专栏之代码审计| Axublog前台SQL注入到后台GetShell

0×00 背景 看了cnvd上有师傅发了Axublog 的漏洞,便对该源码进行分析和漏洞复现,在漏洞复现过程发现可以将这些漏洞组合利用从而通过前台SQL...

41270
来自专栏编程坑太多

『中级篇』Docker Cloud自动构建 Docker image(55)

PS:自动化构建的build image,是不是感觉很爽,但是这里的Dockfile这是需要基础牢靠。本节也是以后学习的基础。

19750
来自专栏七夜安全博客

OD常用断点之CC断点

14720
来自专栏Java后端技术栈

简要分析ZooKeeper基本原理

最近一直有小伙伴私信我,问一些关于Zookeeper的知识,下边关于的Zookeeper的知识整理了一下,一起学习一下。

8630
来自专栏黑白安全

Git 曝任意代码执行漏洞,所有使用者都受影响

Git 由于在处理子模块代码库的设置档案存在漏洞,导致开发者可能遭受任代码执行攻击,多数代码托管服务皆已设置拒绝有问题的代码储存库,但建议使用者尽快更新,避免不...

8810
来自专栏我是攻城师

实时收集Storm日志到ELK集群

49640
来自专栏从零开始学自动化测试

python笔记23-unittest单元测试之mock

unittest.mock是一个用于在Python中进行单元测试的库,Mock翻译过来就是模拟的意思,顾名思义这个库的主要功能是模拟一些东西。 它的主要功能是使...

23620

扫码关注云+社区

领取腾讯云代金券