在lucene中,为了防止并发操作引起索引文件的破坏,因此引入了锁的机制,其原理跟操作系统中的互斥锁是一致的。那么,lucene是如何实现锁机制的呢?

我们可以看到lucene.Net中有Lucene.Net.Store.Lock这样一个类,它是一个抽象,定义了lucene中锁需要实现的一些基本操作。

 

1 public abstract class Lock
2 {
3     public abstract bool Obtain();
4 
5     public abstract void  Release();
6         
7     public abstract bool IsLocked();
8 }

 

上面可以看到Lock类中包含三个操作,三个函数的功能是分别是锁定资源、释放资源和判断资源是否已被锁定(lucene中需要锁定的资源一般是指某个文件或某个目录)。下面一段代码是Lucene.Net2.0中,Lucene.Net.Store.FSDirectory.AnonymousClassLock对lock的一种实现。

 

 1 private class AnonymousClassLock : Lock
 2 {
 3     override public bool IsLocked()
 4     {
 5         if (Lucene.Net.Store.FSDirectory.disableLocks)
 6             return false;
 7         bool tmpBool;
 8         if (System.IO.File.Exists(lockFile.FullName))
 9             tmpBool = true;
10         else
11             tmpBool = System.IO.Directory.Exists(lockFile.FullName);
12         return tmpBool;
13     }
14 
15     public override bool Obtain()
16     {
17         if (Lucene.Net.Store.FSDirectory.disableLocks)
18             return true;
19 
20         bool tmpBool;
21         if (System.IO.File.Exists(Enclosing_Instance.lockDir.FullName))
22             tmpBool = true;
23         else
24             tmpBool = System.IO.Directory.Exists(Enclosing_Instance.lockDir.FullName);
25         if (!tmpBool)
26         {
27             try
28             {
29                 System.IO.Directory.CreateDirectory(Enclosing_Instance.lockDir.FullName);
30             }
31             catch (Exception)
32             {
33                 throw new System.IO.IOException("Cannot create lock directory: " + Enclosing_Instance.lockDir);
34             }
35         }
36 
37         try
38         {
39             System.IO.FileStream createdFile = lockFile.CreateNewFile();
40             createdFile.Close();
41             return true;
42         }
43         catch (Exception)
44         {
45             return false;
46         }
47     }
48 
49     public override void Release()
50     {
51         if (Lucene.Net.Store.FSDirectory.disableLocks)
52             return;
53         bool tmpBool;
54         if (System.IO.File.Exists(lockFile.FullName))
55         {
56             System.IO.File.Delete(lockFile.FullName);
57             tmpBool = true;
58         }
59         else if (System.IO.Directory.Exists(lockFile.FullName))
60         {
61             System.IO.Directory.Delete(lockFile.FullName);
62             tmpBool = true;
63         }
64         else
65             tmpBool = false;
66         bool generatedAux = tmpBool;
67     }
68 }
69 

 

上面的代码中,Enclosing_Instance.lockDir.FullName是需要锁定的文件或者目录的完整路径,而lockFile则是需要创建的锁文件,文件后缀名是".lock"。"AnonymousClassLock中的Obtain方法实际上就是创建lockFile文件,Release就是删除lockFile文件。那么Obtain只是创建一个lockFile文件,为什么就实现了加锁的功能呢?

我们来看第39行的代码:System.IO.FileStream createdFile = lockFile.CreateNewFile();

这行代码是被我修改过的,Lucene.Net2.0的原来的调用的函数是lockFile.Create()。CreateNewFile和Create函数的作用都是创建一个文件,为什么要改成CreateNewFile呢?这两个函数有一个很重要的区别,CreateNewFile执行时,如果需要创建的文件已存在,就会引发异常,而Create函数在文件已存在是仍然能正常执行。明白两个函数间的区别,就不难理解它的加锁作用了。当一个对象获取某个资源时,给该资源加锁创建了lockFile文件,另一对象试图获取该对象的访问权时,由于lockFile已存在,调用CreateNewFile就会引发异常,直至另一个对象调用Release函数删除lockFile文件。(CreateNewFile这个函数实际上在.NET中是没有的,而在java中存在,这里这么写是为了方便理解。.NET中,实现CreateNewFile的写法应该是lockFile.Open(System.IO.FileMode.CreateNew),而源码中的Create()写法应该算是个2.0的bug吧,2.4已经修正了)

Lucene.Net2.0中lock的实现是依赖于CreateNewFile函数的互斥性。这样的做法,存在一个问题。当应用程序锁定某一个目录,因为某个异常或者其他原因导致程序异常退出,lockFile没有被删除,程序第二次运行时,则无法获得目录的访问权。下面我们通过一段代码来验证这个问题。

1 public void Test()
2 {
3       string path = @"d:\test\";
4       Lucene.Net.Analysis.Analyzer analyzer = new Lucene.Net.Analysis.SimpleAnalyzer();
5       Lucene.Net.Index.IndexWriter writer = new Lucene.Net.Index.IndexWriter(path, analyzer, false);
6       throw new Exception();
7 }

 

第一次运行上面的代码后,在throw new Exception()处抛出异常中止。然后重新运行这段代码,则会看到Lock obtain timed out异常,这是由于第一次异常退出lockFile没有被删除,重新运行后进入死锁。当然,如果是程序自己出现异常是可以通过try...catch...finally代码处理,但是如果出现断电或死机等其他情况呢,那就只有手动去删除lockFile文件。

Lucene通过创建lockFile文件的方式实现了互斥锁,而.NET中同样也有lock,那么能否用.NET中的锁替代lucene的锁呢?答案是否定的。.NET中的锁主要是针对线程安全而设计的,而Lucene中的锁是为了保护索引文件,不仅仅是多线程中的问题,同一线程中也有互斥问题。如果采用.NET的锁机制,那么在同一个线程中将能够创建两个IndexWriter,由于IndexWriter在内存中会缓存一部分document,其中一个IndexWriter发生Merge操作,无法获取到另一IndexWriter缓存的document,则会引起异常,可能导致索引文件被破坏。

 

 posted on 2010-03-06 23:53  Yann  阅读(1299)  评论(0编辑  收藏  举报