Spiga

您善于使用匿名函数吗?

2008-04-04 00:10 by Jeffrey Zhao, 14131 visits, 网摘, 编辑

尝试从缓存中获取数据,如果数据存在则返回,否则从数据源中获取数据,放入缓存,然后返回。

  您是否熟悉上面这段逻辑说明?如果您的应用中大量使用了缓存,则上面这段逻辑很可能会出现许多次。例如:

CacheManager cacheManager = new CacheManager();
 
public List<User> GetFriends(int userId)
{
    string cacheKey = "friends_of_user_" + userId;
 
    object objResult = cacheManager.Get(cacheKey);
    if (objResult != null) return (List<User>)objResult;
 
    List<User> result = new UserService().GetFriends(userId);
    cacheManager.Set(cacheKey, result);
 
    return result;
}

  这段逻辑似乎比较简单,不过在实际应用中,从数据源中获取数据可能不是简单地调用一个方法,而是需要多个类之间的协作,事务控制等等,而缓存的读写可能也会比上面的示例来的复杂。因此,一个可读性高的做法是提供三个独立的方法(读取缓存,读取数据源,写入缓存),使得一个拥有缓存的方法只需要简单地实现上面所提到的读写逻辑即可。

  正如文章开头所说,如果您的应用中大量使用了缓存,则上面这段逻辑很可能会出现许多次。在一定程度上这种重复也是多余的,违背了DRY原则。因此我们设法提供一个基类,把这段缓存读写逻辑封装起来:

public abstract class CacheReader<T>
{
    /// <summary>从缓存中获取数据</summary>
    /// <param name="data">从缓存中取得的数据</param>
    /// <returns>从缓存中成功取得数据则返回true,反之则false</returns>
    public abstract bool GetFromCache(out T data);
 
    /// <summary>从数据源获取数据</summary>
    /// <returns>从数据源取得的对象</returns>
    public abstract T ReadFromSource();
 
    /// <summary>将数据写入缓存</summary>
    /// <param name="data">将要写入缓存的数据</param>
    public abstract void SetToCache(T data);
 
    public T Read()
    {
        T data;
        if (this.GetFromCache(out data)) return data;
 
        data = this.ReadFromSource();
        this.SetToCache(data);
 
        return data;
    }
}

  于是我们将这段缓存读写逻辑集中到了CacheReader类的Read方法中。而对于每个缓存读写操作,我们只要实现一个CacheReader类的子类,提供三个抽象方法的具体实现即可。如下:

private class GetFriendCacheReader : CacheReader<List<User>>
{
    private int m_userId;
    private string m_cacheKey;
    private CacheManager m_cacheManager;
 
    public GetFriendCacheReader(int userId, CacheManager cacheManager)
    {
        this.m_userId = userId;
        this.m_cacheKey = "friends_of_user_" + userId;
        this.m_cacheManager = cacheManager;
    }
 
    public override bool GetFromCache(out List<User> data)
    {
        object objData = this.m_cacheManager.Get(this.m_cacheKey);
        if (objData == null)
        {
            data = null;
            return false;
        }
 
        data = (List<User>)objData;
        return true;
    }
 
    public override List<User> ReadFromSource()
    {
        return new UserService().GetFriends(this.m_userId);
    }
 
    public override void SetToCache(List<User> data)
    {
        this.m_cacheManager.Set(this.m_cacheKey, data);
    }
}

  于是我们的GetFriends方法就可以修改成如下模样:

public List<User> GetFriends(int userId)
{
    return new GetFriendCacheReader(userId, cacheManager).Read();
}

  典型的“模板方法(Template Method)”模式的应用,真是……优雅?这是明显的“矫枉过正”!一个GetFriends方法需要开发一个类,而应用中少说也该有几十上百个这样的方法吧?于是乎,我们吭哧吭哧地开发了几十上百个CacheReader的子类,每个子类需要把所有用到的对象和数据封装进去,并且实现三个抽象方法——OMG,真可谓“OOP”到了极致!

  还好,我们可以使用匿名方法。为此,我们写一个Helper方法:

public static class CacheHelper
{
    public delegate bool CacheGetter<TData>(out TData data);
 
    public static TData Get<TData>(
        CacheGetter<TData> cacheGetter,
        Func<TData> sourceGetter,
        Action<TData> cacheSetter)
    {
        TData data;
        if (cacheGetter(out data))
        {
            return data;
        }
 
        data = sourceGetter();
        cacheSetter(data);
 
        return data;
    }
}

  委托是个好东西,可以作为方法的参数使用,而匿名方法的存在让这种方式变得尤其有用。例如,我们现在就可以:

public List<User> GetFriends(int userId)
{
    string cacheKey = "friends_of_user_" + userId;
 
    return CacheHelper.Get(
        delegate(out List<User> data) // cache getter
        {
            object objData = cacheManager.Get(cacheKey);
            data = (objData == null) ? null : (List<User>)objData;
 
            return objData != null;
        },
        () => // source getter
        {
            return new UserService().GetFriends(userId);
        },
        (data) => // cache setter
        {
            cacheManager.Set(cacheKey, data);
        });
 
}

  看上去是不是有点古怪?其实习惯了就好。这种做法有好处还不少:

  • 可读性好:操作的逻辑被分割在不同block中。
  • 编程方便:能够直接使用方法的参数和外部对象,不会有封装的麻烦。
  • 调试方便:设置断点之后可以轻松看出“从缓存中读取”、“从数据源读取”和“写入缓存”的过程。

  在出现匿名方法之后,这种将委托作为参数传入方法的做法其实已经非常普遍了。例如在微软推出的并行库中就能使用同样的调用方式:

void ParallelCalculate()
{
    double result = 0;
    object syncObj = new object();
 
    List<int> list = GetIntList();
 
    Parallel.For<double>(
        0,
        list.Count,
        () => 0,
        (index, ps) =>
        {
            int value = list[index];
            for (int n = 0; n < 100; n++)
            {
                ps.ThreadLocalState += Math.Sqrt(value) * Math.Sin(value);
            }
        },
        (threadResult) =>
        {
            lock (syncObj)
            {
                result += threadResult;
            }
        });
 
    Console.WriteLine("result = " + result);
}

  您接受这种做法了吗?

  您善于使用匿名函数吗?

Add your comment

118 条回复

    评论共2页: 上一页 1 2 
  1. #101楼 volnet(可以叫我大V)      2008-04-04 02:57
    @东哥
    3.0 3.5中都是额外的功能的,但他们的编译后的代码还是2.0的,也就是类似于文章中有Lambda与过去使用的delegate的方式所编译成的目标代码都是基于2.0的,因为.NET框架是基于静态编译的代码,因此用2.0即可解释IL了……
      回复  引用  查看    
  2. #102楼 东哥      2008-04-04 03:18
    @volnet(可以叫我大V)
    明白了,谢谢解答,今天晚上终于能睡踏实了,冥思苦想了好几天,终于有您给解答了.
      回复  引用  查看    
  3. #103楼 曲滨*銘龘鶽      2008-04-04 03:54
    代码很有趣!讲解的很细致;

    个人感觉 Lambda 表达式,和正则表达式一样都不是正常语意,如果写的太多看着很不舒服,感觉还是带 delegate 关键字匿名函数看着舒服点。
      回复  引用  查看    
  4. #104楼 5254341[未注册用户]2008-04-04 08:12
    1,大牛们都是白天睡觉晚上工作?
    2,大牛们压根就不睡觉?
      回复  引用    
  5. #105楼 dusonchen[未注册用户]2008-04-04 08:36
    你的解决方法有点像:将大量的子类变成大量的方法而已。个人第一感觉而已。-_-
      回复  引用    
  6. #106楼 李战      2008-04-04 09:19
    http://www.cnblogs.com/Emoticons/qface/055242240.gif" alt="" />嗯,一步一地看,好不容明白了那个“优雅”的模板,刚叫完好!http://www.cnblogs.com/Emoticons/qface/055243929.gif" alt="" />~~~~~结果又被小老赵当头一棒,打晕了。http://www.cnblogs.com/Emoticons/qface/055243801.gif" alt="" />


    我等还没明白过神来的时候,只见小老赵祭起一件奇怪的法器,顿时光芒满地,立即扫除一切妖魔鬼怪。http://www.cnblogs.com/Emoticons/qface/055243188.gif" alt="" />

    随即,小老赵淡淡地说:这就是匿名函数!

    然后,留下两个问题,又消失于无影无踪...http://www.cnblogs.com/Emoticons/msn/moon.gif" alt="" />http://www.cnblogs.com/Emoticons/QQ/laf.gif" alt="" />

      您接受这种做法了吗?

      您善于使用匿名函数吗?

    我等凡夫俗子,慢慢领悟吧......http://www.cnblogs.com/Emoticons/yoyocici/223853735.gif" alt="" />

      回复  引用  查看    
  7. #107楼 狼Robot      2008-04-04 09:20
    看得有些晕了...
      回复  引用  查看    
  8. #108楼 Klesh Wong      2008-04-04 11:14
    建议用IList<User>代替List<User>
      回复  引用  查看    
  9. #109楼 飞地      2008-04-04 11:46
    晕晕
      回复  引用  查看    
  10. #110楼[楼主] Jeffrey Zhao      2008-04-04 12:10
    @东哥
    .NET版本推出往往会带有新的语言版本,目前框架和语言双方什么风声都没有,所以一般不会很快推出.NET 4.0
      回复  引用  查看    
  11. #111楼[楼主] Jeffrey Zhao      2008-04-04 12:13
    @Klesh Wong
    良好的做法是,参数尽量用抽象类型,返回值尽量用具体类型。
    这里是返回值,所以List比IList好。
      回复  引用  查看    
  12. #112楼 秋千      2008-04-04 13:03
    慢慢消化
      回复  引用  查看    
  13. #113楼 黑虫[未注册用户]2008-04-04 13:06
    怎么没有CacheManager的方法?


      回复  引用    
  14. #114楼 sand[未注册用户]2008-04-04 16:07
     老赵把代码都贴出来啊...

    Func 与 Action 是哪个函数或方法..CacheManager也没找到.
     对于初学者学起来就更难了
      
      回复  引用    
  15. #115楼[楼主] Jeffrey Zhao      2008-04-04 16:25
    @黑虫
    我随便乱写的,就一个Get和Set方法,就当是最普通的缓存容器。
      回复  引用  查看    
  16. #116楼[楼主] Jeffrey Zhao      2008-04-04 16:25
    @sand
    Func和Action都是.NET Framework里的委托,查一下MSDN吧。
      回复  引用  查看    
  17. #117楼 Cat Chen      2008-04-04 17:20
    写JavaScript写多了就肯定会有写lamda的倾向……
      回复  引用  查看    
  18. #118楼 助燃      2008-04-04 20:36
    今天的《参考消息》里面有一篇文章讲每天睡眠时间不足的人非常容易诱发肥胖,想起老赵每天都很晚睡,于是上来回复一帖跟主题无关的,老赵一定要注意休息啊,身体是革命的本钱,特别是能够传播革命火种的人更是要保养好
      回复  引用  查看    
  19. #119楼 SZW      2008-04-04 22:20
    "吭哧吭哧"太形象了:)
      回复  引用  查看    
  20. #120楼 吉林哥163[未注册用户]2008-04-04 22:30
    老赵,你好,这个问题我MAIL过你,不过不知道你收到没有。
    有一个具体的问题想请教一下,我在一个页面中使用TIMER,向服务器发出请求,通常会执行较长时间,在此期间,我点击已经出来的结果里的链接,即向本应用程序中的另一个页面发出请求,这个请求会被阻塞,直至父页面的TIMER长任务完全完成。请问这个问题怎么解决。
    多谢!


    另,我在UpdateProgress中放置BUTTON,用javascript中断了Timer后,的确中断了TIMER,可当页面再次回发时(比如GridView翻页),这个TIMER控件会被重新激活。请问如何能够彻底关掉这个Timer.

    如有了解这方面的,还请不吝赐教.
      回复  引用    
  21. #121楼 私家侦探[未注册用户]2008-04-04 23:13
    @吉林哥163
    看过大文件上传并且进度条显示百分比的实现吗,没有柱塞哦,那应该可以解决你的问题吧
    看看效果吧 www.aspnetupload.com

    第二个问题可以判断翻页的url参数让Timer不工作
      回复  引用    
  22. #122楼 私家侦探[未注册用户]2008-04-04 23:18
    其实匿名方法在java世界早主流了,我在学校里面整天碰到的的就是java的匿名方法或者内部类唉
      回复  引用    
  23. #123楼 啊东      2008-04-05 00:40
    好文。。
      回复  引用  查看    
  24. #124楼 吉林哥163[未注册用户]2008-04-05 01:21
    @私家侦探
    感谢你的回复。。
    第一个问题,那个大文件上传的例子似乎不太好使,并且我中间点击CANCEL时出错,关键是它的程序逻辑就不是并行的,感觉应该是传一部分,作一下回显,再接着传,类似TIMER。可是我说的情况是手动的在一个回传正在进行时,发起另一个请求。
    第二个问题,看似可以解决此问题,但其实不然,因为我的TIMER是要TICK过N次(N不确定)的,在客户端没有中止它的时候,我翻页也不应该中止这个TIMER,但是客户端中止它,似乎无法通知到服务器端。
      回复  引用    
  25. #125楼 fox23      2008-04-05 02:10
    lambda方便是方便,只是多行就不好调试了,这个应该是编译器一个可以改进的地方
      回复  引用  查看    
  26. #126楼 ASP.NET CMS[未注册用户]2008-04-05 13:43
    暂时还不习惯这样的写法,呵呵,只能慢慢习惯了
    不过这篇文章写的恶很好,支持下
      回复  引用    
  27. #127楼[楼主] Jeffrey Zhao      2008-04-05 17:07
    --引用--------------------------------------------------
    私家侦探: 其实匿名方法在java世界早主流了,我在学校里面整天碰到的的就是java的匿名方法或者内部类唉
    --------------------------------------------------------
    java中没有匿名方法,不过有内部类,所以可以使用内联的写法实现一个接口,得到我这篇文章中类似的做法。
      回复  引用  查看    
  28. #128楼[楼主] Jeffrey Zhao      2008-04-05 17:08
    --引用--------------------------------------------------
    fox23: lambda方便是方便,只是多行就不好调试了,这个应该是编译器一个可以改进的地方
    --------------------------------------------------------
    lambda是一行代码,的确不容易调试。
    我倒不期望这个,不过很希望调试器能够在Watch这种地方使用lambda表达式,呵呵。
      回复  引用  查看    
  29. #129楼[楼主] Jeffrey Zhao      2008-04-05 17:09
    @吉林哥163
    Timer?是哪里的Timer?setTimeout?如果要去除的话用clearTimeout就可以了。
      回复  引用  查看    
  30. #130楼[楼主] Jeffrey Zhao      2008-04-05 17:10
    --引用--------------------------------------------------
    助燃: 今天的《参考消息》里面有一篇文章讲每天睡眠时间不足的人非常容易诱发肥胖,想起老赵每天都很晚睡,于是上来回复一帖跟主题无关的,老赵一定要注意休息啊,身体是革命的本钱,特别是能够传播革命火种的人更是要保养好
    --------------------------------------------------------
    最近瘦了十多斤……聊胜于无……
      回复  引用  查看    
  31. #131楼 私家侦探[未注册用户]2008-04-05 21:38
    ---------引用-----------------------------
    @私家侦探
    感谢你的回复。。
    第一个问题,那个大文件上传的例子似乎不太好使,并且我中间点击CANCEL时出错,关键是它的程序逻辑就不是并行的,感觉应该是传一部分,作一下回显,再接着传,类似TIMER。可是我说的情况是手动的在一个回传正在进行时,发起另一个请求。
    第二个问题,看似可以解决此问题,但其实不然,因为我的TIMER是要TICK过N次(N不确定)的,在客户端没有中止它的时候,我翻页也不应该中止这个TIMER,但是客户端中止它,似乎无法通知到服务器端。
    ---------------------引用----------------------
    老赵,你好,这个问题我MAIL过你,不过不知道你收到没有。
    有一个具体的问题想请教一下,我在一个页面中使用TIMER,向服务器发出请求,通常会执行较长时间,在此期间,我点击已经出来的结果里的链接,即向本应用程序中的另一个页面发出请求,这个请求会被阻塞,直至父页面的TIMER长任务完全完成。请问这个问题怎么解决。
    -----------------------------------------------

    你是说:服务器能够一边计算一边显示部分结果?我很怀疑,除非用了特殊技术.在提交过程中(注意:还在提交噢)服务器是不可能把部分结果显示到同一页面的任何地方,即使在该页面嵌入一个iframe,然后不断的刷新去获取放在session的部分计算结果值,即会发生你说的"柱塞".

    如果非要在提交时获取部分结果值,使用的特殊技术httpHandlers,可以web.config配置文件里面配置.把获取部分结果的页面配置为另外一个http处理者去处理,这样就变成有人在计算结果有人在旁边看结果,可以把"结果"暂时放会话中,这样就不会"柱塞"拉,哈哈畅通!!

    以上仅供参考,嘿嘿!

    @老赵
    java确实没有匿名方法,应该是匿名类啊,在java的c/s事件编程即awt或swing,不想用都不行
      回复  引用    
  32. #132楼 吉林哥163[未注册用户]2008-04-06 00:13
    @Jeffrey Zhao
    Timer是指Asp.net Ajax中的Timer服务器控件.
    我是用这种方式中止它的。
    function CancelSearch()
    {
    if (prm.get_isInAsyncPostBack())
    {
    prm.abortPostBack();
    var timer = $find("<%=this.Timer1.ClientID %>");
    timer.set_enabled(false);
    timer._stopTimer();
    }
    }
    个人感觉,那句set_enabled(false)其实没起到什么作用。

    @私家侦探

    具体描述一下,我作一个长查询,Timer每TICK一次,就多得到一些数据,然后把数据绑定到GridView中(绑定前,我会把这个DataTable放到Session中),在GridView里最后一列是“详情”链接,点击它,就开个新IE窗口,根据传递过去的ID,查看这个数据的详情。全部详情数据都来自该条记录(在Session中的DataTable里)。这样,问题就出现了,由于是异步提交,我在一次TIMER TICK过程中,可以点击“详情”,期望的结果是它应该立即显示出来,可事实就是,它一定要等父窗体的异步提交执行完成后,才会执行子窗体(经过设断点跟踪发现,直到父窗口的异步提交完成,子窗口的Page_Load才开始执行)。这就是我说的阻塞。
      回复  引用    
  33. #133楼 fff[未注册用户]2008-04-06 11:29
    CacheHelper.Get后面的<>里不写是因为可以推断出来吗?
      回复  引用    
  34. #134楼 txink[未注册用户]2008-04-06 12:37
    头晕!
      回复  引用    
  35. #135楼 私家侦探[未注册用户]2008-04-06 14:58
    @吉林哥163
    你确定你已经实现同一页面一边提交一边显示数据吗?
    那我真的很崇拜你。据我猜测Asp.net Ajax中的Timer的底层应该也是js的settimeout一类的东西,或许你的意思可能一小段时间的定时提交吧,比如两秒提交一次,然后结果数据绑定到页面,然后再提交,再绑定,一直到定时器终止,对吧。如果是这样的话,可能是定时器设置间隔得太快了,来不及反应,而不是柱塞。你这个需求到很奇怪啊,呵呵,你们经理该大pp了。

    或者用另外一种方案吧,即拦截器(java中很常见的):
    在web.config配置httpHandlers节点:
    --------------------------
    <httpHandlers>
    <add verb="*" path="你那个详情链接的网址" type="MyHandler.NewHandler,MyHandler"/>
    </httpHandlers>
    --------------------------
    * 代表拦截所有提交提交,path代表拦截的网址,type代表被拦截到的网址的处理类

    这样就不会柱塞了,因为“详情”页面是用另外一个http请求处理器处理的
    你再搜索httpHandlers的资料看看吧,需要一点功底的
      回复  引用    
  36. #136楼 吉林哥163[未注册用户]2008-04-06 18:39
    @私家侦探
    首先感谢!
    如你所述,我的要求恰好就是在提交过程中显示,因此TIMER间隔越短,越能模拟我的要求,可惜最短就是1ms,呵呵。
    我就是要在Page1的异步回发过程中,点击Page1中的Link1(a href="Page2.aspx?id=XXX"),然后在Page2里显示id为xxx的数据。

    如果如你所说,Timer时间设置太短,“来不及反应”,这不就相当于是同一个会话不能并行提交多个请求吗?而我想要的就是两者互不干扰(像多线程的感觉一样)。我想请教的就是这个问题。
      回复  引用    
  37. #137楼 私家侦探[未注册用户]2008-04-06 21:02
    @吉林哥163
    你的问题很糟糕,嘿嘿.
    你试着把间隔时间拉长点,再试试.
    我已经试过在同一页面中是不可能提交未结束时把数据显示出来的,不管是使用本页面或者iframe或者模式对话框来显示那些"部分结果",都是会柱塞的,边提交边显示都做不到了更别提你还想点击那些"部分结果"中的"详情"链接.

    没有在网页上做过多线程.好像实现不了,页面提交的过程整个页面的元素就被"封"住了呀,唉
      回复  引用    
  38. #138楼 私家侦探[未注册用户]2008-04-06 21:12
    @吉林哥163
    把你的案例再说清楚点,比如查询前页面有哪些元素,要实现什么样的效果
    或许我能用httpHandler的并行技术处理你的问题,其实page的基类就是一个httpHandler,所以每个页面都能处理http请求,当然你也可以自己写http请求处理类,在特殊地方就是要这么干,很多ajax控件就是这么做的,进度条也是这么做的
      回复  引用    
  39. #139楼 MS的明天      2008-04-06 21:42
    一直喜欢这样的风格
      回复  引用  查看    
  40. #140楼 吉林哥163[未注册用户]2008-04-06 23:09
    @私家侦探
    你说的并行httpHandler我感觉大概可能会解决我的问题,虽然我并不了解它的具体细节。我去了解一下先。
    你看一下www.cooseek.com(比较购物搜索引擎),就明白我的意思了。
      回复  引用    
  41. #141楼 peace      2008-04-06 23:39
    尝试从缓存中获取数据,如果数据存在则返回,否则从数据源中获取数据,放入缓存,然后返回

    这个所说的缓存 到底能缓存多长时间呢?
      回复  引用  查看    
  42. #142楼 吉林哥163[未注册用户]2008-04-06 23:54
    @私家侦探
    我想这应该是asp.net执行模型的默认行为,N年前,我试验过用Page中启动一个线程(无论是否显式的设置为后台线程或非后台线程),如果想导航到另外一个页面(在t.Start()之后Response.Redirect(newPage)),它都一定要等那个线程执行完毕后,才可能导航过去,这说明asp.net下如此使用多线程是不可行的,后来我用分进程的方式(把长任务放到一个web service中,在web service里再启动一个线程)来实现的。

    试想一下,如果在Page1的后台代码还在执行的过程中,同一个session又请求page2,势必有可能引起混乱,比如对公共的session,application,全局变量等的读写会引起难以预期的错误。可现在我的确需要这样做,还请多指教。haitaohua@163.com,泡泡(常在线),MSN都是这个账号
      回复  引用    
  43. #143楼[楼主] Jeffrey Zhao      2008-04-07 01:12
    --引用--------------------------------------------------
    peace: 尝试从缓存中获取数据,如果数据存在则返回,否则从数据源中获取数据,放入缓存,然后返回

    这个所说的缓存 到底能缓存多长时间呢?
    --------------------------------------------------------
    缓存的“功能”其实很简单,就是把一段内容保留一段时间,这个时间自然是根据业务性质来决定的。当然在内存不够时,缓存容器也会去除一些它认为不太重要的内容。
      回复  引用  查看    
  44. #144楼[楼主] Jeffrey Zhao      2008-04-07 01:13
    --引用--------------------------------------------------
    fff: CacheHelper.Get后面的&lt;&gt;里不写是因为可以推断出来吗?
    --------------------------------------------------------
    编译器会负责确定所有这些。
      回复  引用  查看    
  45. #145楼[楼主] Jeffrey Zhao      2008-04-07 01:15
    @吉林哥163
    lock,最常用的方案,呵呵。
      回复  引用  查看    
  46. #146楼[楼主] Jeffrey Zhao      2008-04-07 01:15
    @吉林哥163
    发现你们一会儿再谈浏览器的线程,一会儿再说asp.net的线程,很乱阿……
      回复  引用  查看    
  47. #147楼 吉林哥163[未注册用户]2008-04-07 01:21
    @Jeffrey Zhao
    能说的稍微详细点儿吗?谢谢:)

    我们好象没谈浏览器的线程啊..
      回复  引用    
  48. #148楼 peace      2008-04-07 08:48
    HttpContext.Current.Cache.Insert("ReportByTemp", dtUsers, null, DateTime.Now.AddMinutes(10), TimeSpan.Zero); 我之前的缓存是这样用的,这样是指定10分钟 可老赵那没看到你缓存有时间之类的参数啊
      回复  引用  查看    
  49. #149楼[楼主] Jeffrey Zhao      2008-04-07 10:21
    @peace
    不用追究这个细节,这篇文章其实我只是把缓存容器的操作的给抽象成一个读取和一个写入了。
    当然您可以认为我只是“号称”使用了一个“自定义”的缓存容器,如果不指定时间就表示永久保存或者一个默认的过期时间。
      回复  引用  查看    
  50. #150楼 Clark Zheng      2008-04-07 10:36
    高手的排版都很帅,哈,过来拍下马屁
      回复  引用  查看    
  51. #151楼 狼Robot      2008-04-07 12:11
    @吉林哥163
    看你们回复了这么多,我还是不清楚你要实现什么功能,如果只是想启动一个单独的线程,问题应该不是很大,只是要看你要用这个线程来做什么?你说页面中启动线程有问题,如果你是要在页面中启动一个线程的话,你可以在页面中调用其它的[static]类或[static]方法,让别人帮它启动嘛?[只是想法,没具体实现,应该是可以的。]
    你说页面阻塞的问题,你可以在别的类中实现异步方法,页面中直接调用异步,应该就不会阻塞了吧?

    我很菜,只是给你点建议。呵呵。
      回复  引用  查看    
  52. #152楼 狼Robot      2008-04-07 12:21
    @吉林哥163
    看了你说的示例站,大概有个思路可以实现上面的搜索效果,有时间我看能不能试试写个示例代码。
      回复  引用  查看    
  53. #153楼 私家侦探[未注册用户]2008-04-07 12:50
    @吉林哥163

    看了你提供的网址,其实你这个案例是提交完再提交,每次间隔一秒。
    依照你提供的网址,我的解决方案如下:
    1.在web.config配置httphanders节点,处理详细页面
    ---------------------
    ...
    <httpHandlers >
    <add verb ="*" path ="DetailList.aspx" type ="MyHttpHandlers" />
    </httpHandlers>
    </system.web>
    ---------------------

    2.写详细页面的处理类:
    你不必再新建一个详细页面了,因为可以由下面的处理类输出。
    详细页面只显示id
    类名和第一步的配置要对应。
    id都能显示了,其它就自己看着办吧
    -------------------
    public class MyHttpHandlers : IHttpHandler
    {
    public MyHttpHandlers()
    { }
    public void ProcessRequest(HttpContext context)
    {
    context.Response.Write("<h1>" + context.Request.QueryString["id"]);
    context.Response.End();
    }

    public bool IsReusable
    {
    get { return false; }
    }

    }
    --------------------------------------------------
      回复  引用    
  54. #154楼 私家侦探[未注册用户]2008-04-07 13:01
    @所有人

    其实无论如何都不可能 “一个页面在提交中的同时把部分结果数据显示到同一页面”,自己用脑袋想想,提交过程中整个页面都“锁”住了,怎么可能还能显示数据,页面状态又不是处于“呈现”的过程。
    但是如果那些部分结果数据是用iframe或模式对话框装载的话,再去配合httphandler的拦截技术是可以实现边提交边显示数据,iframe我不是很确定,模式对话框我有实现过。这样的效果是有两个http请求处理类在并行处理。
      回复  引用    
  55. #155楼 吉林哥163[未注册用户]2008-04-08 13:24
    @私家侦探
    你的方式我简单试了一下,或许是逻辑非常少的缘故,很快就可以返回,但我总感觉这不是真的并行,另外,因为不是用Page本身来处理请求,所以里面用到的服务器控件如GridView,事件触发等难道都要自己来手工写html。
    后来我又想,假定这个httpHandler已经是并行的处理请求了,那么我在ProcessRequest中做的任何事情应该都与此前的SimpleSearchResult.aspx页面生命周期无关了吧。那么是否包括Response.Redirect这样的逻辑呢,经过试验,发现仍然不是“并行”的效果,我甚至一度怀疑,是否与ProcessRequest中的处理逻辑的复杂度有关?或者是否是因为两者访问了共同的Session变量...

    @Jeffrey Zhao
    没明白你说的lock是什么意思,与我说的情形有什么关系??
      回复  引用    
  56. #156楼[楼主] Jeffrey Zhao      2008-04-08 14:50
    @吉林哥163
    处理并发访问公用数据的最常见的方法当然就是lock了。
      回复  引用  查看    
  57. #157楼 吉林哥163[未注册用户]2008-04-08 18:40
    @Jeffrey Zhao
    呵呵,可能是我表述的稍微有点儿跳跃,引起误会了。。
    我是在猜想为什么本应该由一个线程池中的不同的线程来处理的请求,却是阻塞的,因为这完全是两个不同的页面,只不过PAGE2是通过在PAGE1里点击了一个target=blank的链接而打开的。。

    经过一番查探与试验,网上普遍说的较多的ASP.NET 2.0异步页,自定义线程池,自定义httpHandler都不是解决问题的办法,或许将父页的长任务做成异步页能有点儿作用,不过对于已经是AJAX的实现来讲,逻辑上就更复杂,更难以控制了。。但为了追求理想的效果,也必须得试一下。可是采用哪种方法能够真正解决问题,必须经过有效的测试....可是,我下载的Web Application Stress 用不了,录制时启动的IE打不开页面,真是晕死。

    哪位达人有好用的Web程序测试工具,介绍给我一个,先谢过。。
      回复  引用    
  58. #158楼 私家侦探[未注册用户]2008-04-08 22:06
    @吉林哥163
    我再次试了一下你说的这个比较购物http://www.cooseek.com/,它现在已经实现ajax在边提取部分数据,已经绑定到页面的"详情"链接可以打开的呀.不会柱塞.
    其实它这个底层无非是用ajax获取数据,然后用js追加写到页面,再ajax去获取数据,间隔一秒,如此循环,因为页面的那些表单没有参与提交,静止在那边的,所以是不存在柱塞的,所以"详情"链接按钮是能并行打开的.

    如果你不成功应该还是你的问题,写个小点的例子,用settimeout + ajaxpro控件模拟看看
      回复  引用    
  59. #159楼 私家侦探[未注册用户]2008-04-08 22:40
    怎么好像每人对aspnet的httpHandlers和httomodules发表一点看法啊,就好比net的高级主题:Attribute(设计控件一般会用到),反射一样,是深入学习net的必须的知识点.我先说点,我也刚接触没有久,

    httomodules是aspnet的管道技术,一个请求发到iis,iis就按配置文件定义的httomodule处理类的顺序,把请求按顺序让这些类处理一下,所以是管道技术.

    httpHandler是请求拦截器,在配置文件中同样配置了很多这样的请求处理类(拦截css文件等等),和httpmodule不同的是它是最后面配置的覆盖之前的相同配置,因为该节点有path参数限制,path支持通配符,所以是拦截技术

    运用上面两种技术可以做出很多"即插即用的插件",想用就配上去,不想用就移除掉,最典型的就是很多ajax控件就是利用上面的技术,还有url重写技术.

    可插拔技术和面向切面(AOP)技术在j2ee可流行啊,我是java转行过来的哈哈,我感觉java太繁琐了,很多都手工式的,不过net也把快我搞成傻瓜了哈哈
      回复  引用    
  60. #160楼[楼主] Jeffrey Zhao      2008-04-08 23:20
    @吉林哥163
    作压力测试吗?Microsoft Web Application Stress Tool足够了,不能用就手动写脚本嘛。如果要换一个的话Load Runner……可惜不是免费的。
      回复  引用  查看    
  61. #161楼[楼主] Jeffrey Zhao      2008-04-08 23:22
    @私家侦探
    补充一些不正确的地方。
    HttpModule是asp.net的技术,和iis无关,iis只是把请求发给isapi,而负责asp.net的isapi才开始使用HttpModule。当然如果你是在说IIS7的话,可以使用集成管道模式,使用HttpModule来操作IIS的处理过程,这是没有问题的。
    httphandler其实只是个简单的请求处理类,说“拦截”可能不太妥当。
      回复  引用  查看    
  62. #162楼 吉林哥163[未注册用户]2008-04-09 00:09
    @Jeffrey Zhao
    Microsoft Web Application Stress Tool在你那儿也无法录制吗?
    如果只是改改脚本还好,完全动手写的做法太没有生产力了:(
    那不就失去了工具的意义了吗,唉,真愁人。我总感觉是哪里犯了冲突,这个工具毕竟已经N年了,很多人都在用了啊。
      回复  引用    
  63. #163楼[楼主] Jeffrey Zhao      2008-04-09 10:45
    @吉林哥163
    我用下来完全正常啊
      回复  引用  查看    
  64. #164楼 路过[未注册用户]2008-04-09 12:58
    老赵的编程风格和规范都很差劲嘛,呵呵,勇气可嘉!
      回复  引用    
  65. #165楼[楼主] Jeffrey Zhao      2008-04-09 13:33
    --引用--------------------------------------------------
    路过: 老赵的编程风格和规范都很差劲嘛,呵呵,勇气可嘉!
    --------------------------------------------------------
    是么,呵呵,愿闻其详。不过经过实际检验我的风格和规范都是非常良好的。
      回复  引用  查看    
  66. #166楼 私家侦探[未注册用户]2008-04-09 21:30
    @路过
    老赵的脑袋毕竟不是控制台,哪有那么标准啦..

    使用匿名方法之前最好要预测一下方法是否会再被重用,不然可惨罗
      回复  引用    
  67. #167楼[楼主] Jeffrey Zhao      2008-04-09 22:10
    @私家侦探
    究竟代码风格有何问题?
    // 任何方法自然不可滥用。
      回复  引用  查看    
  68. #168楼 私家侦探[未注册用户]2008-04-10 15:26
    @Jeffrey Zhao
    那我就直言了,就我看主要有三点吧呵呵:
    1.象这种命名似乎有些多余,比如GetFromCache,ReadFromSource,SetToCache ;中间的form和to也许是多余的,没有的话意思也完整, 如果照你的逻辑GetFriends应该改为GetFriendsByUserId

    2.范型类型一会儿用"T",一会儿用"Tdate"

    3.“m_userId”象这种字段命名也比较怪异吧,
    还有这个“object syncObj = new object()”
    “object objResult = cacheManager.Get(cacheKey)”
    呵呵,晕,间隔都不到十行代码
      回复  引用    
  69. #169楼[楼主] Jeffrey Zhao      2008-04-10 16:59
    @私家侦探
    呵呵,
    GetFromCache其实是GetXXXFromCache的缩写,
    对于单个变量来说,T和TData都是可以的(多个就一定需要写明确了),这些可以从MSDN里找出示例来,也都满足.NET编程规范。
    GetFriendsByUserId是多余的,因为参数名已经写清楚是userId了。
    m_userId这种写法是微软内部(某些project team)的标准,很多地方可见(微软内外)。
    当然最后一段代码是从别人的代码中复制过来的,的确不是我的风格,呵呵。

    其实不谦虚地说,我一直是以编程风格和规范优秀“著称”的,所以看到有人说我非常不好我感到很惊讶。:P
      回复  引用  查看    
  70. #170楼 私家侦探[未注册用户]2008-04-12 12:53
    @Jeffrey Zhao
    你有没有想过一个问题,缓存类的机制一就是键值对,但是假设过于巨大的话,虽然缓存会过期,会自动被踢出去,但是假设即使这样,里面还是存在几十万个,那怎么办,会不会很慢?崩溃?

    同样的如果设计一个网站,假设初步评估这个网站的同时在线数量也有几十万,使用session存储状态,那么检索这个session对象会不会变得超慢呢,我暂时不考虑用cookies

    最近在设计一个系统,呵呵,没有办法啦,请教一下
      回复  引用    
  71. #171楼[楼主] Jeffrey Zhao      2008-04-12 14:03
    @私家侦探
    慢是不会慢的,目前的缓存系统性能早就不是问题,最多是东西太多内存不够用。关于这样的问题还是要看业务要求,这和在线数量没有必然联系,主要是看网站需要访问的内容。当然如果在线数量几十万说明规模已经有相当规模了,这时候可以用于缓存的内存资源应该也不会少,许多数据可以放内存。
    至于你说数据量大的缓存,其实不一定要放在内存中,也可以放在硬盘文件中,这样就相当于不会占用内存了。
    还有Session和cookie完全是不能替代的了,至于Session我建议关闭,其实应用中使用缓存一般就可以了,sessionless很重要。
      回复  引用  查看    
  72. #172楼 狼Robot      2008-04-12 20:11
    楼主能不能说下怎么将缓存写到硬盘上?
    学习下.
      回复  引用  查看    
  73. #173楼 狼Robot      2008-04-12 20:15
    另外再问下楼主一个Session占的内存最少是多少?
      回复  引用  查看    
  74. #174楼[楼主] Jeffrey Zhao      2008-04-12 21:32
    @狼Robot
    就是写文件读文件啊。
      回复  引用  查看    
  75. #175楼[楼主] Jeffrey Zhao      2008-04-12 21:32
    @狼Robot
    Session要看放多少东西的,不放东西应该就不占内存吧。
      回复  引用  查看    
  76. #176楼 私家侦探[未注册用户]2008-04-13 11:37
    @Jeffrey Zhao
    谢谢赵老大!

    设计的是目前还算比较流行的那种"在线客服"系统,比如这个在线客户www.53kf.com,他们的客户已经达到了十几万了,并且都是在聊天中.不过他们的服务器应该是集群的,否则早崩溃了,这种超大聊天系统,缓存和cookie估计用得不多

    要解决的问题是:聊天的那个页面如何及时获取对话输入的消息?并且如果对方在输入时能够显示"对方正在输入.."这样的提示.

    显然双方当前输入的消息应该放在内存中,然后双方的聊天页面不断的使用ajax去检查内存中对方是否输入了新消息.以及是否在输入,再追加到聊天页面中.这样的效率才高.

    但是内存估计会消耗很多,因为会员是几十万级的

    如果用数据库维护当前输入的对话消息显然是不妥的,因为要不停的去获取(当然,每对话一次也是要写入数据库),用文件似乎也不妥当,呵呵

    至于你说的Session和cookie完全是不能替代,我认为可以的,有些人为了避免Session耗掉过多服务器内存,就把会员的状态用cookie(保存在客户端浏览器进程中,没有时间期限的cookie)代替,每次调用后台页面时就把在存浏览器进程中的cookie读出来使用,使用完再写入,这样就可以代替session,对不?嘿嘿
      回复  引用    
  77. #177楼 狼Robot      2008-04-13 12:15
    @私家侦探
    .NET下的Session和Cookie是有很大区别的吧,Session好像什么都可以放,但Cookie只能放字符.当然如果你的Session只用来放些字符或数字什么的,那是可以完全替代,Cookie放对象也可以,只是要自己序列化,而且Cookie的大小是有限的.

    @Jeffrey Zhao
    我以前不知道是听人说还是在哪看到,说一个Session占4k内存(没算放了什么东西,又有一说是20k),如果是几十万在线的话,就用4k和50w来算,就去掉了4*50w≈2G的内存.不知道楼主有没有听过4k或20k这个说法?
      回复  引用  查看    
  78. #178楼 deerchao      2008-04-13 15:22
    @狼Robot
    应该是你记错了. 应该是在某些浏览器中单个cookie的大小不能超过4K才对.

    一个空的Session占4K/20K内存,做出这样设计的人应该拉去枪毙 :)
      回复  引用  查看    
  79. #179楼[楼主] Jeffrey Zhao      2008-04-14 08:09
    @私家侦探
    最好的方式是使用Comet,可以了解一下这部分内容。
    或者使用AJAX轮询的方式,其实对于服务器的压力也不会很大。
      回复  引用  查看    
  80. #180楼 私家侦探      2008-04-14 16:59
    @赵老大
    不错,看了一些Comet资料,茅塞顿开,豁然开朗,呵呵,thank you
      回复  引用  查看    
  81. #181楼[楼主] Jeffrey Zhao      2008-04-14 21:34
    @私家侦探
    理念很简单,实现很困难。
      回复  引用  查看    
  82. #182楼 nicye      2008-04-23 15:26
    常用的东西
      回复  引用  查看    
  83. #183楼 Damon King      2008-05-13 10:57
    2.0.XXXX的写法
     1 public List<User> GetFriends(int userId)
     2 {
     3     string cacheKey = "friends_of_user_" + userId;
     4  
     5     return CacheHelper.Get(
     6         delegate(out List<User> data) // cache getter
     7         {
     8             object objData = cacheManager.Get(cacheKey);
     9             data = (objData == null? null : (List<User>)objData;
    10  
    11             return objData != null;
    12         },
    13         delegate() // source getter
    14         {
    15             return new UserService().GetFriends(userId);
    16         },
    17         delegate(List<User> data) // cache setter
    18         {
    19             cacheManager.Set(cacheKey, data);
    20         });
    21  
    22 }
    23 
      回复  引用  查看    
  84. #184楼 haha6000[未注册用户]2008-08-23 16:50
    参数列表这么复杂,确实有些晕。呵呵
      回复  引用    
  85. #185楼 …懿上壹下℡      2008-09-22 09:36
    顶了 慢慢看!
      回复  引用  查看    
  86. #186楼 风中过客      2008-10-12 14:14
    如果函数行数比较多,不觉得使用匿名函数有什么好处,匿名函数应该用在代码行数比较少的情况下,还有事件处理,多线程中或许用的更多些,一般的函数到处用匿名函数会很混乱!
      回复  引用  查看    
  87. #187楼 jay tian      2008-11-02 21:31
    在一定程度上这种重复也是多余的,违背了DRY原则!

    请问,这个DRY,是什么含义?
      回复  引用  查看    
  88. #188楼[楼主] Jeffrey Zhao      2008-11-02 22:33
    @风中过客
    还好吧,匿名函数其实就是个普通的block,混乱在那里呢?
      回复  引用  查看    
  89. #189楼 jackwong[未注册用户]2008-11-20 00:37
    个人并不赞同你的这样的方法。
    匿名方法,从软件工程角度说,应该是不应该使用或者尽量少用。
    缺点很明显:
    破坏了程序的易读性,当然马上就会带来的结果是:程序难于维护。

    因为如果你觉得这样的程序易读的话,那么循环里面不停直接嵌套代码而不是函数和方法的方法也是值得推广的罗?

    从某个特例来看,比如你这个例子来看,似乎有使用的优点,如果推而广之,整个项目都用这种风格编程,有委托的地方,全是匿名,我的天,不敢想了!哈哈

    软件工程实际上为了项目的整体的规范会牺牲绝大部分个体的个性,当然也牺牲个体部分的所谓“方便”和“效能”。但是,这是值得的。

    因此,你的这种方法不值得推广。
      回复  引用    
  90. #190楼[楼主] Jeffrey Zhao      2008-11-20 14:10
    @jackwong
    匿名方法和易不易读无关,C#也是因为匿名方法的高可读性才加上的这个特性的。你说的降低维护性那是因为滥用,再好的东西也经不起滥用,所以我觉得你的“推而广之”等说法都是非常无厘头的。
      回复  引用  查看    
  91. #191楼 553723644[未注册用户]2009-02-27 09:22
    一个字:我靠啊
      回复  引用    
  92. #192楼 fishrere[未注册用户]2009-04-18 14:41
    可读性好:操作的逻辑被分割在不同block中。
    编程方便:能够直接使用方法的参数和外部对象,不会有封装的麻烦。
    调试方便:设置断点之后可以轻松看出“从缓存中读取”、“从数据源读取”和“写入缓存”的过程。

    实在看不出有上面的三项优势。
    可读性好?脑瓜子出问题了吧。
      回复  引用    
  93. #193楼[楼主] Jeffrey Zhao      2009-04-18 15:05
    @fishrere
    您可以保留自己的看法。
      回复  引用  查看    
  94. #194楼 sendreams      2009-04-25 10:18
    就像前面一个人说的:这样的解决方案就是将大量的子类变成大量的方法。代码真的很优雅很好维护吗?我看也未必。
      回复  引用  查看    
  95. #195楼[楼主] Jeffrey Zhao      2009-04-25 17:02
    --引用--------------------------------------------------
    sendreams: 就像前面一个人说的:这样的解决方案就是将大量的子类变成大量的方法。代码真的很优雅很好维护吗?我看也未必。
    --------------------------------------------------------
    如果大量子类都只有2-3行实质代码的时候,您的感觉是什么呢?
      回复  引用  查看    
  96. #196楼 Gray Zhang      2009-04-25 20:03
    我还是喜欢接口加实现,无他,只因为类可以通过配置文件配置,可以序列化并传递,可惜委托似乎不行
      回复  引用  查看    
  97. #197楼[楼主] Jeffrey Zhao      2009-04-25 21:15
    --引用--------------------------------------------------
    Gray Zhang: 我还是喜欢接口加实现,无他,只因为类可以通过配置文件配置,可以序列化并传递,可惜委托似乎不行
    --------------------------------------------------------
    不同需求不同对待方式,你有配置生成的需求,当然就不能用委托了。
    但是,我从来没有遇到过需要配置的文章所说的场景。
    而且就算有,文章说的方式也有很多用武之地,呵呵。
      回复  引用  查看    
  98. #198楼 Colin Han      2009-06-08 20:06
    重读这篇文章后,突然想到,只有加上C# 4.0的命名参数,上面的代码才能够真的算做优雅的代码。
    否则,如果没有注释的话,上面的代码只能算作巧妙,而不能算作优雅。同时,注释本身就不优雅 :)
      回复  引用  查看    
  99. 评论共2页: 上一页 1 2 



发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1136953




历史上的今天:
2007-04-04 让UpdatePanel支持文件上传(1):开始

相关文章:

相关链接: