diff --git a/TextLocator/App.xaml.cs b/TextLocator/App.xaml.cs index d2945e04cbce1f417a4a7e12832bb9bb253dee04..503ed0cec7799776123cfc2b398a83394f9eb686 100644 --- a/TextLocator/App.xaml.cs +++ b/TextLocator/App.xaml.cs @@ -35,7 +35,7 @@ namespace TextLocator // HTML或XML服务 FileInfoServiceFactory.Register(FileType.HTML和XML类型, new XmlFileService()); // 常用图片服务 - FileInfoServiceFactory.Register(FileType.常用图片, new DevelopFileService()); + FileInfoServiceFactory.Register(FileType.常用图片, new ImageFileService()); // 程序员服务 FileInfoServiceFactory.Register(FileType.代码文件, new DevelopFileService()); // 纯文本服务 diff --git a/TextLocator/Index/LuceneIndexCore.cs b/TextLocator/Index/LuceneIndexCore.cs index 24d438c31a41eb502f4dec09c3b8f55e47314e15..b4ba243eeeceda2987daeb033254f03dab467696 100644 --- a/TextLocator/Index/LuceneIndexCore.cs +++ b/TextLocator/Index/LuceneIndexCore.cs @@ -8,6 +8,8 @@ using System.Threading; using TextLocator.Core; using TextLocator.Enums; using TextLocator.Factory; +using TextLocator.Index; +using TextLocator.Service; using TextLocator.Util; namespace TextLocator.Index @@ -23,13 +25,23 @@ namespace TextLocator.Index /// 状态回调委托 /// /// - public delegate void StatusCallback(string status); + public delegate void Callback(string status); + + /// + /// 锁 + /// + private static object locker = new object(); + + /// + /// 已完成数量 + /// + private static volatile int finishCount = 0; /// /// 创建索引 /// /// 获得的文档包 - public static void CreateIndex(List filePaths, bool rebuild, StatusCallback callback) + public static void CreateIndex(List filePaths, bool rebuild, Callback callback) { // 判断是创建索引还是增量索引(如果索引目录不存在,重建) bool create = !Directory.Exists(AppConst.APP_INDEX_DIR); @@ -40,26 +52,65 @@ namespace TextLocator.Index } // 索引写入初始化(FSDirectory表示索引存放在硬盘上,RAMDirectory表示放在内存上) - Lucene.Net.Index.IndexWriter writer = new Lucene.Net.Index.IndexWriter(AppConst.INDEX_DIRECTORY, AppConst.INDEX_ANALYZER, create, Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED); + Lucene.Net.Index.IndexWriter indexWriter = new Lucene.Net.Index.IndexWriter(AppConst.INDEX_DIRECTORY, AppConst.INDEX_ANALYZER, create, Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED); // 文件总数 int count = filePaths.Count(); - // 遍历读取文件,并创建索引 - for (int i = 0; i < count; i++) + using (var countDown = new MutipleThreadResetEvent(count)) { - // 文件路径 - string filePath = filePaths[i]; + // 设置线程最大数量 + ThreadPool.SetMaxThreads(24, 48); + // 遍历读取文件,并创建索引 + for (int i = 0; i < count; i++) + { + // 加入线程池 + ThreadPool.QueueUserWorkItem(new WaitCallback(CreateIndexTask), new TaskInfo() { + TotalCount = count, + FilePath = filePaths[i], + Create = create, + IndexWriter = indexWriter, + Callback = callback, + ResetEvent = countDown + }); + } + + // 等待所有线程结束 + countDown.WaitAll(); + countDown.Dispose(); + } + try + { + indexWriter.Dispose(); + } + catch (Exception ex) + { + log.Error(ex.Message, ex); + } + } + + /// + /// 创建索引任务方法 + /// + /// + private static void CreateIndexTask(object obj) + { + TaskInfo taskInfo = obj as TaskInfo; + try + { + string filePath = taskInfo.FilePath; + bool create = taskInfo.Create; + Lucene.Net.Index.IndexWriter indexWriter = taskInfo.IndexWriter; // 临时文件跳过 - if (filePath.IndexOf("~$") >0) + if (filePath.IndexOf("~$") > 0) { - continue; + return; } // 非重建 && 文件已经被索引过 if (!create && !string.IsNullOrEmpty(AppUtil.ReadIni("FileIndex", filePath, ""))) { - continue; + return; } // 写入 @@ -83,57 +134,78 @@ namespace TextLocator.Index // 根据文件路径获取文件类型(自定义文件类型分类) FileType fileType = FileTypeUtil.GetFileType(filePath); - // 文件内容 - string content = FileInfoServiceFactory.GetFileInfoService(fileType) - .GetFileContent(filePath); - - // 缩略信息 - string breviary = new Regex(" |\r|\n|\\s").Replace(content, ""); - if (breviary.Length > 150) + lock (locker) { - breviary = breviary.Substring(0, 150) + "..."; + IFileInfoService fileInfoService = FileInfoServiceFactory.GetFileInfoService(fileType); + + // 文件内容 + string content = fileInfoService.GetFileContent(filePath); + + // 缩略信息 + string breviary = new Regex(" |\r|\n|\\s").Replace(content, ""); + if (breviary.Length > 150) + { + breviary = breviary.Substring(0, 150) + "..."; + } + + // 当索引文件中含有与filemark相等的field值时,会先删除再添加,以防出现重复 + indexWriter.DeleteDocuments(new Lucene.Net.Index.Term("FileMark", fileMark)); + + Lucene.Net.Documents.Document doc = new Lucene.Net.Documents.Document(); + // 不分词建索引 + doc.Add(new Lucene.Net.Documents.Field("FileMark", fileMark, Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.NOT_ANALYZED)); + doc.Add(new Lucene.Net.Documents.Field("FileType", fileType.ToString(), Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.NOT_ANALYZED)); + doc.Add(new Lucene.Net.Documents.Field("FileSize", fileSize + "", Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.NOT_ANALYZED)); + doc.Add(new Lucene.Net.Documents.Field("Breviary", breviary, Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.NOT_ANALYZED)); + doc.Add(new Lucene.Net.Documents.Field("CreateTime", createTime, Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.NOT_ANALYZED)); + + // ANALYZED分词建索引 + doc.Add(new Lucene.Net.Documents.Field("FileName", fileName, Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.ANALYZED)); + doc.Add(new Lucene.Net.Documents.Field("FilePath", filePath, Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.ANALYZED)); + doc.Add(new Lucene.Net.Documents.Field("Content", content, Lucene.Net.Documents.Field.Store.NO, Lucene.Net.Documents.Field.Index.ANALYZED)); + + indexWriter.AddDocument(doc); + // 优化索引 + indexWriter.Optimize(); + + finishCount++; } - // 当索引文件中含有与filemark相等的field值时,会先删除再添加,以防出现重复 - writer.DeleteDocuments(new Lucene.Net.Index.Term("FileMark", fileMark)); - - Lucene.Net.Documents.Document doc = new Lucene.Net.Documents.Document(); - // 不分词建索引 - doc.Add(new Lucene.Net.Documents.Field("FileMark", fileMark, Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.NOT_ANALYZED)); - doc.Add(new Lucene.Net.Documents.Field("FileType", fileType.ToString(), Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.NOT_ANALYZED)); - doc.Add(new Lucene.Net.Documents.Field("FileSize", fileSize + "", Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.NOT_ANALYZED)); - doc.Add(new Lucene.Net.Documents.Field("Breviary", breviary, Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.NOT_ANALYZED)); - doc.Add(new Lucene.Net.Documents.Field("CreateTime", createTime, Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.NOT_ANALYZED)); - - // ANALYZED分词建索引 - doc.Add(new Lucene.Net.Documents.Field("FileName", fileName, Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.ANALYZED)); - doc.Add(new Lucene.Net.Documents.Field("FilePath", filePath, Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.ANALYZED)); - doc.Add(new Lucene.Net.Documents.Field("Content", content, Lucene.Net.Documents.Field.Store.NO, Lucene.Net.Documents.Field.Index.ANALYZED)); - - writer.AddDocument(doc); - // 优化索引 - writer.Optimize(); - - string msg = "创建索引:(" + i + " / " + count + ") => 文件:" + filePath + ",耗时:" + (DateTime.Now - beginMark).TotalSeconds + "秒"; + string msg = "索引:[" + finishCount + " / " + taskInfo.TotalCount + "] => 文件:" + filePath + ",耗时:" + (DateTime.Now - beginMark).TotalSeconds + "秒"; // 执行状态回调 - callback(msg); - - log.Debug(msg); - - // 手动GC - GC.Collect(); - GC.WaitForPendingFinalizers(); - } + taskInfo.Callback(msg); - try - { - writer.Dispose(); + log.Debug(msg); } catch (Exception ex) { log.Error(ex.Message, ex); } + finally + { + // 手动GC + GC.Collect(); + GC.WaitForPendingFinalizers(); + + try + { + taskInfo.ResetEvent.SetOne(); + } catch { } + } + } + + /// + /// 任务信息 + /// + class TaskInfo + { + public int TotalCount { get; set; } + public string FilePath { get; set; } + public bool Create { get; set; } + public Lucene.Net.Index.IndexWriter IndexWriter { get; set; } + public LuceneIndexCore.Callback Callback { get; set; } + public MutipleThreadResetEvent ResetEvent { get; set; } } } } diff --git a/TextLocator/Index/MutipleThreadResetEvent.cs b/TextLocator/Index/MutipleThreadResetEvent.cs new file mode 100644 index 0000000000000000000000000000000000000000..7da4c27e9e75af8ff3ff647934213f2f01eefa0f --- /dev/null +++ b/TextLocator/Index/MutipleThreadResetEvent.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace TextLocator.Index +{ + /// + /// 多线程重置事件 + /// + public class MutipleThreadResetEvent : IDisposable + { + private readonly ManualResetEvent done; + private readonly int total; + private long current; + + /// + /// 构造函数 + /// + /// 需要等待执行的线程总数 + public MutipleThreadResetEvent(int total) + { + this.total = total; + current = total; + done = new ManualResetEvent(false); + } + + /// + /// 唤醒一个等待的线程 + /// + public void SetOne() + { + // Interlocked 原子操作类 ,此处将计数器减1 + if (Interlocked.Decrement(ref current) == 0) + { + //当所以等待线程执行完毕时,唤醒等待的线程 + done.Set(); + } + } + + /// + /// 等待所以线程执行完毕 + /// + public void WaitAll() + { + done.WaitOne(); + } + + /// + /// 释放对象占用的空间 + /// + public void Dispose() + { + ((IDisposable)done).Dispose(); + } + } +} diff --git a/TextLocator/MainWindow.xaml.cs b/TextLocator/MainWindow.xaml.cs index ac433cbbe0dcfefa32f459b07ef7abd57c663225..e856dde8d4717e9e1480a55387d2ad84fd5c501b 100644 --- a/TextLocator/MainWindow.xaml.cs +++ b/TextLocator/MainWindow.xaml.cs @@ -154,8 +154,15 @@ namespace TextLocator LuceneIndexCore.CreateIndex(filePaths, rebuild, ShowStatus); } + string msg = "索引执行结束,共用时:" + (DateTime.Now - beginMark).TotalSeconds + "秒"; + // 显示状态 - ShowStatus("索引执行结束,共用时:" + (DateTime.Now - beginMark).TotalSeconds + "秒"); + ShowStatus(msg); + + this.Dispatcher.BeginInvoke(new Action(() => + { + Message.ShowSuccess("MessageContainer", msg); + })); // 构建结束 build = false; @@ -181,8 +188,8 @@ namespace TextLocator private void Search(List keywords, string fileTypeFilter) { string text = ""; - foreach(string kw in keywords) { - text += kw + ","; + foreach(string keyword in keywords) { + text += keyword + ","; } text = text.Substring(0, text.Length - 1); log.Debug("关键词:(" + text + "), 文件类型:" + fileTypeFilter); @@ -304,7 +311,7 @@ namespace TextLocator resultNum++; } - string message = "检索完成!共检索到" + resultNum + "个符合条件的结果(只显示前" + num + "条)。耗时:" + (DateTime.Now - beginMark).TotalSeconds + "秒"; + string message = "检索完成!共检索到" + resultNum + "个符合条件的结果(只显示前" + num + "条)。耗时:" + (DateTime.Now - beginMark).TotalSeconds + "秒。"; Message.ShowSuccess("MessageContainer", message); diff --git a/TextLocator/Properties/AssemblyInfo.cs b/TextLocator/Properties/AssemblyInfo.cs index ebce5a442bd3077fa44346d726db5e04c1bdbceb..61c5dc01a4a674b467e0743105f39ff088092739 100644 --- a/TextLocator/Properties/AssemblyInfo.cs +++ b/TextLocator/Properties/AssemblyInfo.cs @@ -50,7 +50,7 @@ using System.Windows; //通过使用 "*",如下所示: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.1.0.0")] -[assembly: AssemblyFileVersion("1.0.0.5")] +[assembly: AssemblyFileVersion("1.0.0.6")] // log4net [assembly: log4net.Config.XmlConfigurator(Watch = true)] \ No newline at end of file diff --git a/TextLocator/Resource/ext/code.png b/TextLocator/Resource/ext/code.png index 7a187d851e02ee604253b11d58c97c6fd128683c..6c78e441ad7330788c836905db9a1c3475f22da0 100644 Binary files a/TextLocator/Resource/ext/code.png and b/TextLocator/Resource/ext/code.png differ diff --git a/TextLocator/Resource/ext/jpg.png b/TextLocator/Resource/ext/jpg.png index 418dd44f992b15be62b61b6fd2befec40b70271c..65fde8f648a39aa66235c5515355baab96bc0a0f 100644 Binary files a/TextLocator/Resource/ext/jpg.png and b/TextLocator/Resource/ext/jpg.png differ diff --git a/TextLocator/TextLocator.csproj b/TextLocator/TextLocator.csproj index fa3ae14080d1c53af6d51fc1eb353bc12a0230f1..ad3b88bfe80880bcbb26f46548ce1369af417c41 100644 --- a/TextLocator/TextLocator.csproj +++ b/TextLocator/TextLocator.csproj @@ -169,6 +169,7 @@ FolderWindow.xaml +