对RAII的思考

对RAII的思考

Some random thoughts about RAII

写程序时常常需要申请系统资源,比如打开文件,申请一块内存。申请到这些资源后,在程序退出或者资源使用完毕后,应当正确的释放。如果不能正确释放,会造成一系列问题。比如申请的内存没有释放造成内存泄露Memory Leak,申请的进程锁没有被解锁Unlock,造成进程间的死锁DeadLock。在C++语言里,解决这类资源管理问题的管用手法是RAII (Resource Acuiquistion Is Initialization)

这篇笔记是对RAII的一点思考。

 

1. 什么是RAII

简单来讲,把获取资源的代码放到类的构造函数里,把释放资源的代码放到析构函数里。比如用ofstream file(“output.txt”) 可以打开文件,当file变量不起作用是,文件会被自动关闭。

比如下面这张图(from:The RAII Programming Idiom),看看这里面有多少地方需要写释放资源的代码。如果使用RAII,这些其实地方都不用留代码。

RAII  Example
RAII Example

 

 

2. RAII的优缺点

RAII的好处是利用C++语言优势安全、正确的管理资源。同时RAII是C++建议的资源获取方式,这种代码可以被广大C++用户理解。

不方便之处是,使用RAII有一些陷阱。比如不要用RAII一次获取多个资源。

 

3.为什么C++有RAII

C++语言保证了一个类构造之后,析构函数会被自动调用。这个使用方式与资源管理的方式相似。因此可以用类的生命周期来管理资源。

 

4. 为什么C/Java/Python没有RAII

C语言没有原生的构造和析构函数,获取的资源不能有任何自动机制来释放。

Java/Python有语言中的支持,即Dispose Pattern。举例来说就是 try…catch..finally语句。使用者只要把释放资源的语句写到finally,资源就会被释放。

 

4. RAII 和Exception的关系

RAII和Exception紧密相关,更确切的说,构造函数和异常这两个特性在某种程度上互相依赖。

对于构造函数来说(获取资源的语句在构造函数里),构造函数没有返回值,因此想知道资源是否成功获取是不能从函数返回值来判断的,唯一可以用的手法是在资源获取失败时抛出异常。也就是说构造函数需要使用异常。

另一方面,使用异常之后,需要用构造函数来管理资源。因为异常抛出以后,很可能处理异常的代码和异常发生的代码不在一个层次(异常在Call Stack上逐层向上)。为了实现异常安全(Exception Safe),应该使用构造函数(另一个选择是智能指针,但智能指针有智能指针的问题,详见C++FAQ的讨论)。

对已有的C++代码来说,实现或检查代码是不是异常安全不是一个的简单人物。这种情况下,异常这个特性往往会被禁用(比如Google C++ style guide)。如果异常被禁用了,我们就没法从构造函数本身获知资源是否成功获取,那是不是说我们没法使用RAII特性呢?

答案是否定的。我们可以在获取资源后,用其他的类函数来检查资源获取是否成功。比如ostream::is_open()就可以检查文件是否被正常打开。

 

5. 怎么绕开RAII

在C等不提供RAII支持的语言里,可以直接绕开RAII,即保证获取资源后,程序的每一个出口都有释放资源的语句。

这种方法有可能造成多处重复的资源释放代码,或者使用goto语句把所有程序跳转到一处资源释放代码。

 

6. 实践中怎么用RAII

实践中除了把资源获取的语句写到构造函数,把资源释放的语句写到析构函数,还应当注意:

1)获取多个资源时,可以写在多个类的构造函数里,使得每一个类的构造函数对应一个资源。这样在任何资源获取失败时,已经获得的资源会得到释放

2)有时候获取资源失败等于程序失败(Fatal Condition),这种情况下可以直接退出(exit),把清理资源的任务留给操作系统。

 

给作者编号

给作者编号

Author Number

http://zhanxw.com/author

 

最近写了一个网页,主要解决写文章给作者编号的问题。这个问题怎么来的呢?现在的文章需要提供作者,作者工作单位和作者的贡献,但是提供的方式是给每个作者单位按照作者出现的顺序编号。比如我的文章有79个作者,现在突然要加一个新的合作者,把他拍到第50位,那么从第51位到最后一位作者的编号可能都需要改变。这个过程很容易出错。我想写个软件给所有人用,顺便凑个热闹,学习一下jQuery + Django,最后的作品放到我的网页(link)。在这个过程中,还学习了Nginx, Emacs web-mode, 这篇笔记主要是把我的经验记录下来。

 

1. 学习Django

Django是Python语言实现的Web架构,它最开始是用于展示新闻(Newroom),国内的豆瓣也用Django。作为Python的“粉丝”,我学一下Django,希望以后可以把有用的工具放到网上。

想入门,最好最省时间的方法是Django tutorial,就在Django的官方网页。这个Tutorial一共有六个部分,介绍的常用Django的功能,包括: 模型(Model),模板(Template),视图(View),静态文件(Static),管理界面(Admin),网址分发(URL Dispatching)等等。学习之后,我感到Django的强项是利用Python语言做到来简化数据库使用。对于一个简单的App,基本开发的流程是: (1)设定project 的settings.py 和urls.py,注册新的app ;(2)设定urls.py来确定网址和视图(View)的关系;(3)用HTML开发模板(/template/app/XXX.html)同时实现各种需要的视图(views.py)

架设Django,一般简易使用uWSGI。我使用的是Nginx处理静态页面,同时用uWSGI处理Django相关界面。简易先在本机用uWSGI调好程序,再放到服务器上并配置Nginx和uWSGI。

Djano默认使用Unicode,而我们一般都用str(),这是ASCII编码。两种编码对于字符串处理来讲(包括join, index)没有什么区别,但在print的时候,Unicode的字符应该先转成UTF-8字符,比如: print u”aaa”.encode(‘UTF-8’)

 

2. 学习jQuery

用jQuery的主要目的是用Ajax来更新网页数据,这样用户用起来有行云流水的感觉。jQuery有.get 和 .post两个方法,使用起来和访问网页很相似。不过这里面有几个陷阱。第一个是GET方式适合小数据,对于Django来讲,默认的大小是4096个字符,大一点的数据应该用POST;第二个陷阱就是POST,为了安全(CSRF: cross-site request forgery),Django要求POST的数据必须有csrftoken,一般的HTML表格Form必须有csrf的标记才会被Django接收。

为了调试Ajax的输入输出,简易用Firefox下的FireBug或者最新版本的Firefox,它们都可以显示Ajax请求的参数和返回值。最新版本的Firefox还可以给Javascript设置断点,这就更加降低了调试的难度。

jQuery的功能很多,这个网页把jQuery 1.9以及之前版本的功能用不同颜色区分出来,很方便查询(link)。

在这个网页中,我还用了handsontable,这是一个在jQuery的基础上开发的实用的javascript spreadsheet,语法简单,使用方便。

3. 一点Nginx经验

对Nginx来讲,我们的配置是用location语句来把特定的网址传给uWSGI进程。注意Nginx设置里如果有多个location语句,Nginx并不一定选择最先匹配的网址,而是选择最长的网址。比如:

location ~* /author/static/.+.(jpg|jpeg|gif|css|png|js|ico|xml)$ {
root /var/django/zhanxw/; # STATIC_ROOT
expires 30d;
}

location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico|xml)$ {
access_log        off;
expires           30d;
root /var/www;
}

如果第一个location写成: “location /author/static”,那么.jpg之类的文件会被第二个location处理,这就不是Django static文件的正确处理方式。

 

4. 一点Emacs web-mode经验

用Emacs写Django的模板HTML,最好用的不是django-html-mode,而是web-mode。只有这个mode可以识别 “{% static ‘polls/index.html’ %}”这样的记号,并正确缩进。

此外web-mode可以自动补全HTML tab,比如你在<p>后面打</,web-mode会帮你补全</p>。还可以用C-c C-e b 和C-c C-e e跳到一对tag的最前面(beginning)和最后面(end)。

 

5. 一般性经验

最开始设计应该以最少功能,最小实现为好,不要一下子把界面设计复杂。可以想几个用例(Use Case),保证最基本最重要的功能,其他功能应该越少越好。

网页的布局应该少用<br/>这种硬回车。在有Bootstrap的情况下,完全可以用<div>和<p>来用更少的HTML tag来实现更自然、自动的布局。