[新闻] 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 来处理。
Global site tag (gtag.js) - Google Analytics