我正在使用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'
}
答案 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;
}
}
注意:
Open
和Close
方法来创建/处理编写器(处理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); });