[新闻] DB4D项目
qiezi
2007-05-11
http://code.google.com/p/db4d/
这是我前段时间开始的一个BerkeleyDB的D语言封装项目,最近dxpcom的D语言封装我打算重写,工作量比较大,所以先放下它继续db4d项目。 db4d和C版本的最大差别是使用异常风格、自动资源释放、使用D数组类型取代DBT类型、限制flag参数以适合IDE提示(如果有好的IDE的话)。 目前遇到比较麻烦的2个问题,可能在其它项目中也很容易遇到。 1、数组内存拷贝。 从DB读取的数据长度未知,所以先分配一个缓冲区就可能太小(异常)或太大(浪费),好的办法当然是让DB自己分配内存,它也提供了这样的选项。 问题来了,这块内存需要拷贝到数组中,然后释放掉,这降低了性能。如果不拷贝内存,那么使用完以后谁来释放?显然只能由使用者释放,又回到C/C++老路子上了。 目前的代码大致是这样的: char* p = malloc(300); // 在BDB内部分配的 char[] arr = p[0..300].dup; free(p); 我在想如果数组有这样一个方法就好了: char* p = malloc(300); char[] arr(p, 300); // 或char arr[](p, 300); // 或: char[] arr; arr.manage(p, 300); 这就成了一个拖管过程,数组在释放时将使用free而不是从gc中移除。我觉得这是个不错的建议,能提给Walter就好了。自己看了一下DMD代码,还不知道如何给数组添加方法呢。 2、用GC处理资源面临的问题也比较严峻,比如一个Database打开一个游标,游标的析构函数里调用了close(为了防止忘记调用),如果是先关闭了Database会怎么样? 这个问题比较复杂,我想了一下,唯一能让它正确处理的方式是每打开一个游标,就在Database中注册一下,如果手动close了就在Database中删除。这样Database在close时,会close所有游标对象。 当然这个方法效率低,还要处理线程同步,也比较繁琐。哪位对这方面有好的想法交流一下? |
|
oldrev
2007-05-11
1. 为什么不用GC阿,降低一点效率也是值得的,用 new ubyte[] 就行了
2. 加一个方法 close,在 ~this() { close(); } 里执行,当然, close 要允许多次无副作用的调用 我的办法是学.net,为这类非GC托管资源统一实现 interface IClosable { void close(); } 方法 |
|
qiezi
2007-05-11
oldrev 写道 1. 为什么不用GC阿,降低一点效率也是值得的,用 new ubyte[] 就行了
目前就是内存拷贝的,当然会放入GC,不是有性能降低嘛。如果是new出来,前面已经说了,我该new多大?太大了浪费,太小了装不下。 我这里是建议在数组内部保存一个标识字段,用来区分数组释放或重新分配时是该让GC回收内存,还是直接free。当然这个就得编译器去实现了。 oldrev 写道 2. 加一个方法 close,在 ~this() { close(); } 里执行,当然, close 要允许多次无副作用的调用 这个。。。自然是已经这么做了的。 oldrev 写道 我的办法是学.net,为这类非GC托管资源统一实现 interface IClosable { void close(); } 方法 依然没有解决我所说的问题。不是close不了,而是close的时机问题,加个接口并不是特别必要的。 class Database { Cursor openCursor() {...} } class Cursor { } scope auto db = new Database; Cursor cursor = db.openCursor(); db很快释放掉了,cursor要等到GC时,而这时可能会: 1、db已经强地关闭了,再关闭cursor可能导致系统挂掉。 2、cursor打开着,db不强行关闭,可能导致关闭操作异常。 这个必须在db析构时,自动把已经打开的cursor给关闭掉。涉及到资源的地方应该是经常碰到这类问题吧,GUI编程也会遇到,当然现在的面向对象的GUI编程通常不会真的创建那么多窗口,而是一个窗口上画其它控件,但依旧会有这个问题吧。 |
|
qiezi
2007-05-11
这种情况还是忽略不处理吧,不然太麻烦了。如果用户总是不手动close,这个资源占用的问题比上面更严重。
|
|
oldrev
2007-05-11
我看只要在文档里注明推荐手动执行 close 就行了,非GC资源尽快释放这应该是处理这类问题的原则
.net 也有这种问题,好像就是这么干的,C# 不是也有个 using() 用法么 |
|
oldrev
2007-05-11
我没有用过 bdb,估计应该有个查询字段大小的函数吧
|
|
oldrev
2007-05-11
前面我讲过了, close() 一定要允许多次无副作用的调用,也就是说在 dtor 里绝对不能有异常。
可以参考这篇文章: 清理非托管资源 http://msdn2.microsoft.com/zh-cn/library/498928w2(VS.80).aspx 其实 GC 也很麻烦,让资源分成了自动和手动两种。 |
|
qiezi
2007-05-11
oldrev 写道 我没有用过 bdb,估计应该有个查询字段大小的函数吧
没有的,你有2种方式,一种是自己分配内存并在查询时告诉它有多大,如果小了它会返回错误。另一种是告诉它由它分配内存,这个通常不会错误。它这么设计是有道理的,减少调用次数,因为每次调用都要锁表或锁记录,除非你用事务。 |
|
qiezi
2007-05-11
oldrev 写道 前面我讲过了, close() 一定要允许多次无副作用的调用,也就是说在 dtor 里绝对不能有异常。
close允许多次调用当然是应该的,而且很容易实现。我可以把错误异常捕捉,不让它在析构函数中抛出,但我讨论的问题不在这里。。。而是析构顺序的问题,如何保证cursor在db关闭前关闭。 oldrev 写道 可以参考这篇文章: 清理非托管资源 http://msdn2.microsoft.com/zh-cn/library/498928w2(VS.80).aspx 其实 GC 也很麻烦,让资源分成了自动和手动两种。 看过了,实际上如果cursor使用scope(类似它的using),或者是显式调用close(类似它的Dispose),就没什么问题了,看来它也没能解决这个。 |
|
oldrev
2007-05-11
qiezi 写道 close允许多次调用当然是应该的,而且很容易实现。我可以把错误异常捕捉,不让它在析构函数中抛出,但我讨论的问题不在这里。。。而是析构顺序的问题,如何保证cursor在db关闭前关闭。 既然cursor 依赖database,那它必然保存有database的引用,你必须确保 cursor 除 close 之外的成员执行时database尚未关闭,这个可以在cursor的每个方法的先验条件中加入 assert(!database.closed()) 来保证。 .net的做法是扔出 ObjectDisposedException,但是我觉得没必要,因为这个是程序员的错误,可以用 assert 来处理。 |