在代码中使Lucene.Net线程安全

时间:2017-01-23 18:55:13

标签: c# multithreading search lucene.net

我正在使用Lucene.Net进行搜索,并想知道如何处理这个线程问题。

我有一个类Test的实例,但是在这种情况下搜索器不是线程安全的,因为定时器线程可以在提供请求的同时更新索引,并且我确实看到了异常。关于如何使其线程安全的任何指针。

DECLARE @param1 int;
DECLARE @param1 VARCHAR(10);

SET @param1 = 10;
SET @param2 = 'CCDOS87'

@Stmt = 'SELECT Field1 FROM TableName WHERE Field1 = ' 
        + CAST(@param1 AS VARCHAR(100)) 
        + ' AND Field1 = ''' 
        + param2 
        + '''';  <- This is a single '

      @stmt = N'INSERT INTO [dbo].[Error_table]
                SELECT DISTINCT 
                xxx AS column1,
                xxx as Column2,
                xxx AS Column3,
                xxx AS Column4,
                xxx AS Environment
                FROM #A A INNER JOIN table1 on A.Column1 = Table1.Column2'

}

2 个答案:

答案 0 :(得分:7)

很多事情&#34;错误&#34;有这个。

正如已经提到的,锁定不安全(你需要锁定读取和写入)。

更重要的是,在Lucene中有更好的方法来处理这个问题。首先,IndexWriter本身就是线程安全的。它应该是Directory的所有者。它通常是一种不良的做法&#34;打开/关闭目录的不同部分。

NRT(近实时)索引的样式涉及从IW获取IndexReader,而不是包装目录。

您的示例中使用的样式实际上只是#34;良好&#34;如果索引基本上是只读的,并且可以每天/每周批量重新生成等。

我重写了这个例子来展示一些方法。显然,由于这只是测试代码,因此根据用例需要重构/增强的细微差别......

public class Test
{
    private static object syncObj = new object();

    private System.Threading.Timer timer;

    private Searcher searcher;

    private IndexWriter writer;
    private IndexReader reader;

    public Test()
    {
        writer = new IndexWriter(new RAMDirectory(), new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30), true, IndexWriter.MaxFieldLength.LIMITED);
        reader = writer.GetReader();
        searcher = new IndexSearcher(reader);
        timer = new System.Threading.Timer(Timer_Elapsed, null, TimeSpan.Zero, TimeSpan.FromMinutes(3));
    }


    public void CreateDocument(string title, string content)
    {
        var doc = new Document();
        doc.Add(new Field("A", title, Field.Store.YES, Field.Index.NO));
        doc.Add(new Field("B", content, Field.Store.YES, Field.Index.ANALYZED));

        writer.AddDocument(doc);
    }

    public void ReplaceAll(Dictionary<string, string> es)
    {
        // pause timer
        timer.Change(Timeout.Infinite, Timeout.Infinite);

        writer.DeleteAll();
        foreach (var e in es)
        {
            AddDocument(e.Value.ToString(), e.Key);
        }

        // restart timer
        timer.Change(TimeSpan.Zero, TimeSpan.FromMinutes(3));
    }

    public List<Document> Search(string queryString)
    {
        var documents = new List<Document>();
        var parser = new QueryParser(Lucene.Net.Util.Version.LUCENE_30, "B", new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30));
        Query query = parser.Parse(queryString);
        int hitsPerPage = 5;
        var collector = TopScoreDocCollector.Create(2 * hitsPerPage, true);
        searcher.Search(query, collector);

        ScoreDoc[] hits = collector.TopDocs().ScoreDocs;

        int hitCount = collector.TotalHits > 10 ? 10 : collector.TotalHits;
        for (int i = 0; i < hitCount; i++)
        {
            ScoreDoc scoreDoc = hits[i];
            int docId = scoreDoc.Doc;
            float docScore = scoreDoc.Score;
            Document doc = searcher.Doc(docId);
            documents.Add(doc);
        }

        return documents;
    }

    private void Timer_Elapsed(object sender)
    {
        if (reader.IsCurrent())
            return;

        reader = writer.GetReader();
        var newSearcher = new IndexSearcher(reader);
        Interlocked.Exchange(ref searcher, newSearcher);
        Debug.WriteLine("Searcher updated");
    }

    public Result ServeRequest(string searchTerm)
    {
        var documents = Search(searchTerm);
        //somelogic
        var result = new Result();

        return result;

    }
}

注意:

  • 作家&#34;拥有&#34;目录
  • 如果这是一个文件库目录,那么您将使用OpenClose方法来创建/处理编写器(处理lock文件)。 RamDirectory可以只是GC&#39; d
  • 使用Interlocked.Exchange代替lock。使用searcher成员(这里是龙!)
  • 时的成本为零
  • 新文档直接添加到作家
  • 如果没有添加新文档,则
  • IsCurrent()允许零费用。根据您添加文档的频率,您可能根本不需要计时器(只需在Timer_Elapsed的顶部调用Search - 显然已重命名)。
  • 请勿使用Optimize()这是以前版本的遗留问题,并且非常不鼓励使用它(性能和磁盘I / O原因)

最后,如果你正在使用Lucene.net v4.8,那么你应该使用SearcherManager(如另一个答案所示)。但是使用取IndexWriter的ctor并将其保留为&#34; singleton&#34; (与writer相同)。它将为您处理锁定和获取新读者。

答案 1 :(得分:1)

而不是使用新的IndexSearcher。您可以使用“SearcherManager”类。

  

SearcherManager _searcherManager = new   SearcherManager(LuceneMapDirectory,null);

并搜索如下:

_searcherManager.ExecuteSearch(searcher =>
        {
          //Execute query using <searcher>
        }, ex => { Trace.WriteLine(ex); });