其实说到上一篇,我们要说的task的知识也说的差不多了,这一篇我们开始站在理论上了解下“线程池”和“任务”之间的关系,不管是

说线程还是任务,我们都不可避免的要讨论下线程池,然而在.net 4.0以后,线程池引擎考虑了未来的扩展性,已经充分利用多核微处理器

架构,只要在可能的情况下,我们应该尽量使用task,而不是线程池。

 

首先看一下task的结构

从图中我们可以看出Task.Factory.StartNew()貌似等同于用ThreadPool.QueueUserWorkItem()创建,但是请注意,我是用TPL的形式

使用线程池,要知道task出现以后,一直标榜着以更少的工作量,更低的性能消耗来PK原始线程。

 

     这里简要的分析下CLR线程池,其实线程池中有一个叫做“全局队列”的概念,每一次我们使用QueueUserWorkItem的使用都会产生一个

“工作项”,然后“工作项”进入“全局队列”进行排队,最后线程池中的的工作线程以FIFO的形式取出,效果图类似如下:

这里要值得一提的是,在.net 4.0之后“全局队列”采用了无锁算法,相比以前版本锁定“全局队列”带来的性能瓶颈有了很大的改观。那么任务

委托的线程池不光有“全局队列”,而且每一个工作线程都有”局部队列“,效果图如下

我们的第一反应肯定就是“局部队列“有什么好处,可以考虑这样的情况,当我们new一个task的时候“工作项”就会进去”全局队列”,如果我们的

task执行的非常快,那么“全局队列“就会FIFO的非常频繁,那么有什么办法缓解呢?当我们的task在嵌套的场景下,“局部队列”就要产生效果了,

比如我们一个task里面有3个task,那么这3个task就会存在于“局部队列”中。

 1   class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             var task = Task.Factory.StartNew(() =>
 6             {
 7                 var task1 = Task.Factory.StartNew(Run1);
 8                 var task2 = Task.Factory.StartNew(Run2);
 9                 var task3 = Task.Factory.StartNew(Run3);
10 
11                 Task.WaitAll(new Task[] { task1, task2, task3 });
12             });
13 
14             Console.Read();
15         }
16 
17         public static void Run1() { Thread.Sleep(100000); }
18 
19         public static void Run2() { Thread.Sleep(100000); }
20 
21         public static void Run3() { Thread.Sleep(100000); }
22     }

从图中可以看到,其实“局部队列“起到了一个分流的作用,也叫做”任务内联化“,”局部队列“采用的是”LIFO"的形式,其实这样的形式也是

为了提升性能之用,因为Run3送到“局部队列”中时可能还存在CPU的高速缓存中,所以从“局部队列”中取出来相对来说更快一点,最后的效

果就是Run3要理论上优先于Run2,Run1先执行。

     现在我们再来考虑这样一种情况,比如有两个人,一个人干完了分配给自己的所有活,而另一个人却还有很多的活,从人情上说,闲的人应

该接手点忙的人的活,同样,对应图中“线程2“跑完了“局部队列”中的所有任务,并且同时发现”全局队列“中已经没有可以跑的”任务“了,然而

“线程1”里面还有Run1,Run2,Run3,那么此时“线程2”采用“FIFO”的形式窃取“线程1”里面的任务。

    从上面种种情况我们看到,这些分流和负载都是普通ThreadPool.QueueUserWorkItem所不能办到的,所以说在.net 4.0之后,我们

尽可能的使用TPL,抛弃ThreadPool。

posted @ 2012-05-27 20:41 一线码农 阅读(1291) 评论(5) 编辑

     

      在.net里面异步编程模型由来已久,相信大家也知道Begin/End异步模式和事件异步模式,在task出现以后,这些东西都可以被task包装

起来,可能有人会问,这样做有什么好处,下面一一道来。

 

一: Begin/End模式

1: 委托

    在执行委托方法的时候,我们常常会看到一个Invoke,同时也有一对你或许不常使用的BeginInvoke,EndInvoke方法对,当然Invoke方法

是阻塞主线程,而BeginInvoke则是另开一个线程。

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             var func = new Func<string, string>(i => { return i + "i can fly"; });
 6 
 7             var state = func.BeginInvoke("yes,", Callback, func);
 8 
 9             Console.Read();
10         }
11 
12         static void Callback(IAsyncResult async)
13         {
14             var result = async.AsyncState as Func<string, string>;
15 
16             Console.WriteLine(result.EndInvoke(async));
17         }
18     }

 

下面我们用task包装一下

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             var func = new Func<string, string>(i =>
 6             {
 7                 return i + "i can fly";
 8             });
 9 
10             Task<string>.Factory.FromAsync(func.BeginInvoke, func.EndInvoke, "yes,", null).ContinueWith
11                 (i =>
12                 {
13                     Console.WriteLine(i.Result);
14                 });
15 
16             Console.Read();
17         }
18     }

 

可以看出,task只要一句就搞定,体现了task的第一个优点:简洁。

 

2:流

    我们发现在Stream抽象类中提供了这样两对BeginRead/EndRead,BeginWrite/EndWrite(异步读写)的方法,这样它的n多继承类都可以

实现异步读写,下面举个继承类FileStream的例子。

 1  static void Main(string[] args)
 2         {
 3             var path = "C://1.txt";
 4 
 5             FileStream fs = new FileStream(path, FileMode.Open);
 6 
 7             FileInfo info = new FileInfo(path);
 8 
 9             byte[] b = new byte[info.Length];
10 
11             var asycState = fs.BeginRead(b, 0, b.Length, (result) =>
12             {
13                 var file = result.AsyncState as FileStream;
14 
15                 Console.WriteLine("文件内容:{0}", Encoding.Default.GetString(b));
16 
17                 file.Close();
18 
19             }, fs);
20 
21             Console.WriteLine("我是主线程,我不会被阻塞!");
22 
23             Console.Read();
24         }

 

我们用task包装一下

 1    static void Main(string[] args)
 2         {
 3             var path = "C://1.txt";
 4 
 5             FileStream fs = new FileStream(path, FileMode.Open);
 6 
 7             FileInfo info = new FileInfo(path);
 8 
 9             byte[] b = new byte[info.Length];
10 
11             Task<int>.Factory.FromAsync(fs.BeginRead, fs.EndRead, b, 0, b.Length, null, TaskCreationOptions.None)
12                 .ContinueWith
13                 (i =>
14                 {
15                     Console.WriteLine("文件内容:{0}", Encoding.Default.GetString(b));
16                 });
17 
18             Console.WriteLine("我是主线程,我不会被阻塞!");
19 
20             Console.Read();
21         }

 

其实看到这里,我们并没有发现task还有其他的什么优点,但是深入的想一下其实并不是这么回事,task能够游刃于线程并发和同步,而原始的异步

编程要实现线程同步还是比较麻烦的。

 

     假如现在有这样的一个需求,我们需要从3个txt文件中读取字符,然后进行倒序,前提是不能阻塞主线程。如果不用task的话我可能会用工作线程

去监视一个bool变量来判断文件是否全部读取完毕,然后再进行倒序,我也说了,相对task来说还是比较麻烦的,这里我就用task来实现。

 1     class Program
 2     {
 3         static byte[] b;
 4 
 5         static void Main()
 6         {
 7             string[] array = { "C://1.txt", "C://2.txt", "C://3.txt" };
 8 
 9             List<Task<string>> taskList = new List<Task<string>>(3);
10 
11             foreach (var item in array)
12             {
13                 taskList.Add(ReadAsyc(item));
14             }
15 
16             Task.Factory.ContinueWhenAll(taskList.ToArray(), i =>
17             {
18                 string result = string.Empty;
19 
20                 //获取各个task返回的结果
21                 foreach (var item in i)
22                 {
23                     result += item.Result;
24                 }
25 
26                 //倒序
27                 String content = new String(result.OrderByDescending(j => j).ToArray());
28 
29                 Console.WriteLine("倒序结果:"+content);
30             });
31 
32             Console.WriteLine("我是主线程,我不会被阻塞");
33 
34             Console.ReadKey();
35         }
36 
37         //异步读取
38         static Task<string> ReadAsyc(string path)
39         {
40             FileInfo info = new FileInfo(path);
41 
42             byte[] b = new byte[info.Length];
43 
44             FileStream fs = new FileStream(path, FileMode.Open);
45 
46             Task<int> task = Task<int>.Factory.FromAsync(fs.BeginRead, fs.EndRead, b, 0, b.Length, null, TaskCreationOptions.None);
47 
48             //返回当前task的执行结果
49             return task.ContinueWith(i =>
50             {
51                 return i.Result > 0 ? Encoding.Default.GetString(b) : string.Empty;
52             }, TaskContinuationOptions.ExecuteSynchronously);
53         }
54     }

 

可以看出,task的第二个优点就是:灵活性。

 

这里可能就有人要问了,能不能用开多个线程用read以同步的形式读取,变相的实现文件异步读取,或许我们可能常听说程序优化后,最后出现的

瓶颈在IO上面,是的,IO是比较耗费资源的,要命的是如果我们开的是工作线程走IO读取文件,那么该线程就会一直处于等待状态,不会再接收任

何的外来请求,直到线程读取到文件为止,那么我们能不能用更少的线程来应对更多的IO操作呢?答案肯定是可以的,这里就设计到了”异步IO“的

概念,具体内容可以参照百科:http://baike.baidu.com/view/1865389.htm  ,有幸的是beginXXX,endXXX完美的封装了“异步IO”。

 

二:事件模式

   这个模式常以XXXCompleted的形式结尾,我们在文件下载这一块会经常遇到,这里我也举个例子。

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             WebClient client = new WebClient();
 6 
 7             client.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(client_DownloadFileCompleted);
 8 
 9             client.DownloadFileAsync(new Uri("http://imgsrc.baidu.com/baike/abpic/item/6a600c338744ebf844a0bc74d9f9d72a6159a7ac.jpg"),
10                                    "1.jpg", "图片下完了,你懂的!");
11 
12             Console.WriteLine("我是主线程,我不会被阻塞!");
13             Console.Read();
14         }
15 
16         static void client_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
17         {
18             Console.WriteLine("\n" + e.UserState);
19         }
20     }

 

先前也说了,task是非常灵活的,那么针对这种异步模型,我们该如何封装成task来使用,幸好framework中提供了TaskCompletionSource来帮助

我们快速实现。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.IO;
 6 using System.Threading.Tasks;
 7 using System.Net;
 8 using System.ComponentModel;
 9 
10 namespace ConsoleApplication4
11 {
12     class Program
13     {
14         static void Main()
15         {
16             var downloadTask = DownLoadFileInTask(
17                     new Uri(@"http://www.7720mm.cn/uploadfile/2010/1120/20101120073035736.jpg")
18                     , "C://1.jpg");
19 
20             downloadTask.ContinueWith(i =>
21             {
22                 Console.WriteLine("图片:" + i.Result + "下载完毕!");
23             });
24 
25             Console.WriteLine("我是主线程,我不会被阻塞!");
26 
27             Console.Read();
28         }
29 
30         static Task<string> DownLoadFileInTask(Uri address, string saveFile)
31         {
32             var wc = new WebClient();
33 
34             var tcs = new TaskCompletionSource<string>(address);
35 
36             //处理异步操作的一个委托
37             AsyncCompletedEventHandler handler = null;
38 
39             handler = (sender, e) =>
40             {
41                 if (e.Error != null)
42                 {
43                     tcs.TrySetException(e.Error);
44                 }
45                 else
46                 {
47                     if (e.Cancelled)
48                     {
49                         tcs.TrySetCanceled();
50                     }
51                     else
52                     {
53                         tcs.TrySetResult(saveFile);
54                     }
55                 }
56 
57                 wc.DownloadFileCompleted -= handler;
58             };
59 
60             //我们将下载事件与我们自定义的handler进行了关联
61             wc.DownloadFileCompleted += handler;
62 
63             try
64             {
65                 wc.DownloadFileAsync(address, saveFile);
66             }
67             catch (Exception ex)
68             {
69                 wc.DownloadFileCompleted -= handler;
70 
71                 tcs.TrySetException(ex);
72             }
73 
74             return tcs.Task;
75         }
76     }
77 }

posted @ 2012-05-26 13:21 一线码农 阅读(1292) 评论(7) 编辑

 

    第三篇来的好晚啊,上一篇说了如何向服务器推送信息,这一篇我们看看如何"快好准"的从服务器下拉信息。

    网络上有很多大资源文件,比如供人下载的zip包,电影(你懂的),那么我们如何快速的进行下载,大家第一反应肯定就是多线程下载,

那么这些东西是如何做的呢?首先我们可以从“QQ的中转站里面拉一个rar下来“。

然后用fiddler监视一下,我们会发现一个有趣的现象:

第一:7.62*1024*1024≈7990914  千真万确是此文件

第二:我明明是一个http链接,tmd的怎么变成n多个了?有意思。

好,我们继续往下看,看看这些链接都做了些什么?

最终,我们发现http协议中有一个Conent—Range字段,能够把我们的文件总大小进行切分,然后并行下载,最后再进行合并,大概我们知道

了什么原理,那么,我们强大的C#类库提供了AddRange来获取Http中资源的指定范围。

 

既然进行了切分,那么首先一定要知道文件的ContentLength是多少,如果对http协议比较熟悉的话,当发送一个头信息过去,服务器返回的

头信息中会包含很多东西,此时我们就知道要下载资源的大概情况,这个就有点“兵马未动,粮草先行“的感觉。

 1             var request = (HttpWebRequest)HttpWebRequest.Create(url);
 2 
 3             request.Method = "Head";
 4 
 5             request.Timeout = 3000;
 6 
 7             var response = (HttpWebResponse)request.GetResponse();
 8 
 9             var code = response.StatusCode;
10 
11             if (code != HttpStatusCode.OK)
12             {
13                 Console.WriteLine("下载资源无效!");
14                 return;
15             }
16 
17             var total = response.ContentLength;

 

这里有个决策,到底是以下载量来决定线程数,还是以线程数来决定下载量,由于我们的下载取决于当前的网速,所以在这种场合下更好的方案是

采用后者,这几天在闪存里面两次看到苍老师,肃然起敬,所以决定在不用线程和线程的情况下,看看下载仓老师的速度如何。

图片大小(217.27KB)

View Code
  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Net;
  6 using System.Threading;
  7 using System.Threading.Tasks;
  8 using System.IO;
  9 using System.Collections.Concurrent;
 10 using System.Diagnostics;
 11 using System.Drawing;
 12 
 13 
 14 namespace ConsoleApplication1
 15 {
 16     public class Program
 17     {
 18         public static CountdownEvent cde = new CountdownEvent(0);
 19 
 20         //每个线程下载的字节数,方便最后合并
 21         public static ConcurrentDictionary<long, byte[]> dic = new ConcurrentDictionary<long, byte[]>();
 22 
 23         //请求文件
 24         public static string url = "http://www.pncity.net/bbs/data/attachment/forum/201107/30/1901108yyd8gnrs2isadrr.jpg";
 25 
 26         static void Main(string[] args)
 27         {
 28             for (int i = 0; i < 1; i++)
 29             {
 30                 Console.WriteLine("\n****************************\n第{0}次比较\n****************************", (i + 1));
 31 
 32                 //不用线程
 33                 //RunSingle();
 34 
 35                 //使用多线程
 36                 RunMultiTask();
 37             }
 38 
 39             Console.Read();
 40         }
 41 
 42         static void RunMultiTask()
 43         {
 44             Stopwatch watch = Stopwatch.StartNew();
 45 
 46             //开5个线程
 47             int threadCount = 5;
 48 
 49             long start = 0;
 50 
 51             long end = 0;
 52 
 53             var total = GetSourceHead();
 54 
 55             if (total == 0)
 56                 return;
 57 
 58             var pageSize = (int)Math.Ceiling((Double)total / threadCount);
 59 
 60             cde.Reset(threadCount);
 61 
 62             Task[] tasks = new Task[threadCount];
 63 
 64             for (int i = 0; i < threadCount; i++)
 65             {
 66                 start = i * pageSize;
 67 
 68                 end = (i + 1) * pageSize - 1;
 69 
 70                 if (end > total)
 71                     end = total;
 72 
 73                 var obj = start + "|" + end;
 74 
 75                 tasks[i] = Task.Factory.StartNew(j => new DownFile().DownTaskMulti(obj), obj);
 76             }
 77 
 78             Task.WaitAll(tasks);
 79 
 80             var targetFile = "C://" + url.Substring(url.LastIndexOf('/') + 1);
 81 
 82             FileStream fs = new FileStream(targetFile, FileMode.Create);
 83 
 84             var result = dic.Keys.OrderBy(i => i).ToList();
 85 
 86             foreach (var item in result)
 87             {
 88                 fs.Write(dic[item], 0, dic[item].Length);
 89             }
 90 
 91             fs.Close();
 92 
 93             watch.Stop();
 94 
 95             Console.WriteLine("多线程:下载耗费时间:{0}", watch.Elapsed);
 96         }
 97 
 98         static void RunSingle()
 99         {
100             Stopwatch watch = Stopwatch.StartNew();
101 
102             if (GetSourceHead() == 0)
103                 return;
104 
105             var request = (HttpWebRequest)HttpWebRequest.Create(url);
106 
107             var response = (HttpWebResponse)request.GetResponse();
108 
109             var stream = response.GetResponseStream();
110 
111             var outStream = new MemoryStream();
112 
113             var bytes = new byte[10240];
114 
115             int count = 0;
116 
117             while ((count = stream.Read(bytes, 0, bytes.Length)) != 0)
118             {
119                 outStream.Write(bytes, 0, count);
120             }
121 
122             var targetFile = "C://" + url.Substring(url.LastIndexOf('/') + 1);
123 
124             FileStream fs = new FileStream(targetFile, FileMode.Create);
125 
126             fs.Write(outStream.ToArray(), 0, (int)outStream.Length);
127 
128             outStream.Close();
129 
130             response.Close();
131 
132             fs.Close();
133 
134             watch.Stop();
135 
136             Console.WriteLine("不用线程:下载耗费时间:{0}", watch.Elapsed);
137         }
138 
139         //获取头信息
140         public static long GetSourceHead()
141         {
142             var request = (HttpWebRequest)HttpWebRequest.Create(url);
143 
144             request.Method = "Head";
145             request.Timeout = 3000;
146 
147             var response = (HttpWebResponse)request.GetResponse();
148 
149             var code = response.StatusCode;
150 
151             if (code != HttpStatusCode.OK)
152             {
153                 Console.WriteLine("下载的资源无效!");
154                 return 0;
155             }
156 
157             var total = response.ContentLength;
158 
159             Console.WriteLine("当前资源大小为:" + total);
160 
161             response.Close();
162 
163             return total;
164         }
165     }
166 
167     public class DownFile
168     {
169         // 多线程下载
170         public void DownTaskMulti(object obj)
171         {
172             var single = obj.ToString().Split('|');
173 
174             long start = Convert.ToInt64(single.FirstOrDefault());
175 
176             long end = Convert.ToInt64(single.LastOrDefault());
177 
178             var request = (HttpWebRequest)HttpWebRequest.Create(Program.url);
179 
180             request.AddRange(start, end);
181 
182             var response = (HttpWebResponse)request.GetResponse();
183 
184             var stream = response.GetResponseStream();
185 
186             var outStream = new MemoryStream();
187 
188             var bytes = new byte[10240];
189 
190             int count = 0;
191 
192             while ((count = stream.Read(bytes, 0, bytes.Length)) != 0)
193             {
194                 outStream.Write(bytes, 0, count);
195             }
196 
197             outStream.Close();
198 
199             response.Close();
200 
201             Program.dic.TryAdd(start, outStream.ToArray());
202 
203             Program.cde.Signal();
204         }
205     }
206 }

 

 

      在下面的图中可以看出,我们的资源被分成了n段,在217.27KB的情况下,多线程加速还不是很明显,我们可以试试更大的文件,这里我就

在本地放一个133M的rar文件。

        //请求文件
        public static string url = "http://localhost:56933/1.rar";

现在看一下效果是非常明显的。

posted @ 2012-05-20 13:42 一线码农 阅读(1567) 评论(5) 编辑

     

      日常开发中,相信大家经常会用like去匹配一些数据,同时我们也知道,like往往会导致全表扫描,当数据量越来越大的时候,我们会纠结于

数据库的龟速查找,此时我们必须另寻蹊跷,这时lucene就可以大显身手了。

     首先我们做一个demo,向数据库中插入10w条数据,总共778M。

 

接下来,我们搜索下新闻内容中包含“流行”的记录。

 

 

mmd,检索一下要78s,是谁都要砸了面前的破机子。

下面我们来看看lucene的效果怎么样。下载地址:http://incubator.apache.org/lucene.net/download.html

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using Lucene.Net.Index;
 6 using Lucene.Net.Store;
 7 using Lucene.Net.Analysis.Standard;
 8 using Lucene.Net.Documents;
 9 using System.Data;
10 using System.Diagnostics;
11 using Lucene.Net.Search;
12 
13 using Lucene.Net.QueryParsers;
14 
15 namespace First
16 {
17     class Program
18     {
19         static string path = @"D:\Sample";
20 
21         static void Main(string[] args)
22         {
23             //创建索引
24             CreateIndex();
25 
26             var watch = Stopwatch.StartNew();
27 
28             //搜索
29             IndexSearcher search = new IndexSearcher(path);
30 
31             //查询表达式
32             QueryParser query = new QueryParser(string.Empty, new StandardAnalyzer());
33 
34             //query.parse:注入查询条件
35             var hits = search.Search(query.Parse("Content:流行"));
36 
37             for (int i = 0; i < hits.Length(); i++)
38             {
39                 Console.WriteLine("当前内容:{0}", hits.Doc(i).Get("Content").Substring(0, 20) + "...");
40             }
41 
42             watch.Stop();
43 
44             Console.WriteLine("搜索耗费时间:{0}", watch.ElapsedMilliseconds);
45         }
46 
47         static void CreateIndex()
48         {
49             //创建索引库目录
50             var directory = FSDirectory.GetDirectory(path, true);
51 
52             //创建一个索引,采用StandardAnalyzer对句子进行分词
53             IndexWriter indexWriter = new IndexWriter(directory, new StandardAnalyzer());
54 
55             var reader = DbHelperSQL.ExecuteReader("select * from News");
56 
57             while (reader.Read())
58             {
59                 //域的集合:文档,类似于表的行
60                 Document doc = new Document();
61 
62                 //要索引的字段
63                 doc.Add(new Field("ID", reader["ID"].ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
64                 doc.Add(new Field("Title", reader["Title"].ToString(), Field.Store.NO, Field.Index.ANALYZED));
65                 doc.Add(new Field("Content", reader["Content"].ToString(), Field.Store.YES, Field.Index.ANALYZED));
66 
67                 indexWriter.AddDocument(doc);
68             }
69 
70             reader.Close();
71 
72             //对索引文件进行优化
73             indexWriter.Optimize();
74 
75             indexWriter.Close();
76         }
77     }
78 }

  

 

我靠,448ms,顿时78s黯然失色,当然这个时间是不包含"创建索引“的时间,从时间复杂度上来说,这种预加载索引算是常量。

 

作为入门,简单的介绍下lucene的实现过程,首先lucene主要分成两步:"索引"和"搜索"。

 

一:索引:

相信大家对索引还是比较熟悉的,lucene能够将我们内容切分成很多词,然后将词作为key,建立“倒排索引”,然后放到索引库中,在上面

的例子中,我们看到了索引过程中使用到了IndexWriter,FSDirectory,StandardAnalyzer,Document和Field这些类,下面简要分析下。

 

1:IndexWriter

    我们看到该类有一个AddDocument方法,所以我们认为该类实现了索引的写入操作。

 

2:FSDirectory

    这个就更简单了,提供了索引库的存放位置,比如我们这里的D:\Sample,或许有人问,能不能存放在内存中,在强大的lucene面前当然

可以做到,lucene中的RAMDirectory就可以实现,当然我们的内存足够大的话,还是可以用内存承载索引库,进而提高搜索的效率。

 

3:StandardAnalyzer

   这个算是索引过程中最最关键的一步,也是我们使用lucene非常慎重考虑的东西,之所以我们能搜索秒杀,关键在于我们如何将输入的内容

进行何种形式的切分,当然不同的切分形式诞生了不同的分析器,StandardAnalyzer就是一个按照单字分词的一种分析器,详细的介绍后续文

章分享。

 

4:Document

 在上面的例子可以看到,他是承载field的集合,然后添加到IndexWriter中,有点类似表中的行的概念。

 

5: Field

提供了对要分析的字段进行何种处理,以KV形式呈现。

①:Field.Store.YES, Field.Index.NOT_ANALYZED   表示对索引字段采取:原样保存并且不被StandardAnalyzer进行切分。

②: Field.Store.NO, Field.Index.ANALYZED             不保存但是要被StandardAnalyzer切分。

 

二:搜索

这个比较容易,根据我们输入的词lucene能够在索引库中快速定位到我们要找的词,同样我们可以看到IndexSearcher,QueryParser,Hits。

 

1:IndexSearcher

   这个我们可以理解成以只读的形式打开由IndexWriter创建的索引库,search给QueryParser提供了查询的桥梁。

 

2:QueryParser

   这玩意提供了一个parse方法能够将我们要查找的词转化为lucene能够理解了查询表达式。

 

3:Hits

   这个就是获取匹配结果的一个指针,优点类似C#中的延迟加载,目的都是一样,提高性能。  

 

好了,大体上也就这样,时间不早了,洗洗睡了。嘻嘻。

 

 

posted @ 2012-05-13 00:53 一线码农 阅读(2612) 评论(41) 编辑

   

     承接上一篇,我们继续说下.net4.0中的同步机制,是的,当出现了并行计算的时候,轻量级别的同步机制应运而生,在信号量这一块

出现了一系列的轻量级,今天继续介绍下面的3个信号量 CountdownEvent,SemaphoreSlim,ManualResetEventSlim。

 

一:CountdownEvent

     这种采用信号状态的同步基元非常适合在动态的fork,join的场景,它采用“信号计数”的方式,就比如这样,一个麻将桌只能容纳4个

人打麻将,如果后来的人也想搓一把碰碰运气,那么他必须等待直到麻将桌上的人走掉一位。好,这就是简单的信号计数机制,从技术角

度上来说它是定义了最多能够进入关键代码的线程数。

     但是CountdownEvent更牛X之处在于我们可以动态的改变“信号计数”的大小,比如一会儿能够容纳8个线程,一下又4个,一下又10个,

这样做有什么好处呢?还是承接上一篇文章所说的,比如一个任务需要加载1w条数据,那么可能出现这种情况。

 

加载User表:         根据user表的数据量,我们需要开5个task。

加载Product表:    产品表数据相对比较多,计算之后需要开8个task。

加载order表:       由于我的网站订单丰富,计算之后需要开12个task。

 

先前的文章也说了,我们需要协调task在多阶段加载数据的同步问题,那么如何应对这里的5,8,12,幸好,CountdownEvent给我们提供了

可以动态修改的解决方案。

  1 using System.Collections.Concurrent;
2 using System.Threading.Tasks;
3 using System;
4 using System.Diagnostics;
5 using System.Collections.Generic;
6 using System.Linq;
7 using System.Threading;
8
9 class Program
10 {
11 //默认的容纳大小为“硬件线程“数
12 static CountdownEvent cde = new CountdownEvent(Environment.ProcessorCount);
13
14 static void Main(string[] args)
15 {
16 //加载User表需要5个任务
17 var userTaskCount = 5;
18
19 //重置信号
20 cde.Reset(userTaskCount);
21
22 for (int i = 0; i < userTaskCount; i++)
23 {
24 Task.Factory.StartNew((obj) =>
25 {
26 LoadUser(obj);
27 }, i);
28 }
29
30 //等待所有任务执行完毕
31 cde.Wait();
32
33 Console.WriteLine("\nUser表数据全部加载完毕!\n");
34
35 //加载product需要8个任务
36 var productTaskCount = 8;
37
38 //重置信号
39 cde.Reset(productTaskCount);
40
41 for (int i = 0; i < productTaskCount; i++)
42 {
43 Task.Factory.StartNew((obj) =>
44 {
45 LoadProduct(obj);
46 }, i);
47 }
48
49 cde.Wait();
50
51 Console.WriteLine("\nProduct表数据全部加载完毕!\n");
52
53 //加载order需要12个任务
54 var orderTaskCount = 12;
55
56 //重置信号
57 cde.Reset(orderTaskCount);
58
59 for (int i = 0; i < orderTaskCount; i++)
60 {
61 Task.Factory.StartNew((obj) =>
62 {
63 LoadOrder(obj);
64 }, i);
65 }
66
67 cde.Wait();
68
69 Console.WriteLine("\nOrder表数据全部加载完毕!\n");
70
71 Console.WriteLine("\n(*^__^*) 嘻嘻,恭喜你,数据全部加载完毕\n");
72
73 Console.Read();
74 }
75
76 static void LoadUser(object obj)
77 {
78 try
79 {
80 Console.WriteLine("当前任务:{0}正在加载User部分数据!", obj);
81 }
82 finally
83 {
84 cde.Signal();
85 }
86 }
87
88 static void LoadProduct(object obj)
89 {
90 try
91 {
92 Console.WriteLine("当前任务:{0}正在加载Product部分数据!", obj);
93 }
94 finally
95 {
96 cde.Signal();
97 }
98 }
99
100 static void LoadOrder(object obj)
101 {
102 try
103 {
104 Console.WriteLine("当前任务:{0}正在加载Order部分数据!", obj);
105 }
106 finally
107 {
108 cde.Signal();
109 }
110 }
111 }


我们看到有两个主要方法:Wait和Signal。每调用一次Signal相当于麻将桌上走了一个人,直到所有人都搓过麻将wait才给放行,这里同样要

注意也就是“超时“问题的存在性,尤其是在并行计算中,轻量级别给我们提供了”取消标记“的机制,这是在重量级别中不存在的,比如下面的

重载public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken),具体使用可以看前一篇文章的介绍。

 

二:SemaphoreSlim

     在.net 4.0之前,framework中有一个重量级的Semaphore,人家可以跨进程同步,咋轻量级不行,msdn对它的解释为:限制可同时访问

某一资源或资源池的线程数。关于它的重量级demo,我的上一个系列有演示,你也可以理解为CountdownEvent是 SemaphoreSlim的功能加

强版,好了,举一个轻量级使用的例子。

 1 using System.Collections.Concurrent;
2 using System.Threading.Tasks;
3 using System;
4 using System.Diagnostics;
5 using System.Collections.Generic;
6 using System.Linq;
7 using System.Threading;
8
9 class Program
10 {
11 static SemaphoreSlim slim = new SemaphoreSlim(Environment.ProcessorCount, 12);
12
13 static void Main(string[] args)
14 {
15 for (int i = 0; i < 12; i++)
16 {
17 Task.Factory.StartNew((obj) =>
18 {
19 Run(obj);
20 }, i);
21 }
22
23 Console.Read();
24 }
25
26 static void Run(object obj)
27 {
28 slim.Wait();
29
30 Console.WriteLine("当前时间:{0}任务 {1}已经进入。", DateTime.Now, obj);
31
32 //这里busy3s中
33 Thread.Sleep(3000);
34
35 slim.Release();
36 }
37 }


同样,防止死锁的情况,我们需要知道”超时和取消标记“的解决方案,像SemaphoreSlim这种定死的”线程请求范围“,其实是降低了扩展性,

所以说,试水有风险使用需谨慎,在觉得有必要的时候使用它。

 

三: ManualResetEventSlim

     相信它的重量级别大家都知道是ManualReset,而这个轻量级别采用的是"自旋等待“+”内核等待“,也就是说先采用”自旋等待的方式“等待,

直到另一个任务调用set方法来释放它。如果迟迟等不到释放,那么任务就会进入基于内核的等待,所以说如果我们知道等待的时间比较短,采

用轻量级的版本会具有更好的性能,原理大概就这样,下面举个小例子。

 1 using System.Collections.Concurrent;
2 using System.Threading.Tasks;
3 using System;
4 using System.Diagnostics;
5 using System.Collections.Generic;
6 using System.Linq;
7 using System.Threading;
8
9 class Program
10 {
11 //2047:自旋的次数
12 static ManualResetEventSlim mrs = new ManualResetEventSlim(false, 2047);
13
14 static void Main(string[] args)
15 {
16
17 for (int i = 0; i < 12; i++)
18 {
19 Task.Factory.StartNew((obj) =>
20 {
21 Run(obj);
22 }, i);
23 }
24
25 Console.WriteLine("当前时间:{0}我是主线程{1},你们这些任务都等2s执行吧:\n",
26 DateTime.Now,
27 Thread.CurrentThread.ManagedThreadId);
28 Thread.Sleep(2000);
29
30 mrs.Set();
31
32 Console.Read();
33 }
34
35 static void Run(object obj)
36 {
37 mrs.Wait();
38
39 Console.WriteLine("当前时间:{0}任务 {1}已经进入。", DateTime.Now, obj);
40 }
41 }

 

posted @ 2012-04-08 15:53 一线码农 阅读(1400) 评论(11) 编辑
摘要: 在并行计算中,不可避免的会碰到多个任务共享变量,实例,集合。虽然task自带了两个方法:task.ContinueWith()和Task.Factory.ContinueWhenAll()来实现任务串行化,但是这些简单的方法远远不能满足我们实际的开发需要,从.net 4.0开始,类库给我们提供了很多的类来帮助我们简化并行计算中复杂的数据同步问题。大体上分为二种:① 并发集合类: 这个在先前的文章中也用到了,他们的出现不再让我们过多的关注同步细节。② 轻量级同步机制: 相对于老版本中那些所谓的重量级同步机制而言,新的机制更加节省cpu的额外开销。关于并发集合类没什么好讲的,如...阅读全文
posted @ 2012-04-07 23:07 一线码农 阅读(1270) 评论(3) 编辑
摘要: 相信在.net平台下,我们都玩过linq,是的,linq让我们的程序简洁优美,简直玩的是爱不释手,但是传统的linq只是串行代码,在并行的年代如果linq不支持并行计算那该是多么遗憾的事情啊。 当然linq有很多种方式,比如linq to sql ,xml,object 等等,如果要将linq做成并行还是很简单的,这里我就举一个比较实际一点的例子,我们知道为了更快的响应用户操作,码农们想尽了各种办法,绞尽了脑汁,其中有一个办法就是将数据库数据预加载到内存中,然后通过各种数据结构的手段来加速CURD,是的,比如一个排序地球人只能做到N(lgN),那么如果我还想再快一点的话该怎么办呢?那么现在..阅读全文
posted @ 2012-04-04 01:05 一线码农 阅读(1863) 评论(12) 编辑
摘要: 在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于“任务的编程模型”所冲击,因为task会比thread具有更小的性能开销,不过大家肯定会有疑惑,任务和线程到底有什么区别?1:任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行。2:任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很小 的开销和精确的控制。一:Task1. 最简单的使用 开启task有两种方式:<1> 实例化Task1 //第一种方式开启2 var ...阅读全文
posted @ 2012-04-03 01:47 一线码农 阅读(2488) 评论(16) 编辑
摘要: 随着多核时代的到来,并行开发越来越展示出它的强大威力,像我们这样的码农再也不用过多的关注底层线程的实现和手工控制,要了解并行开发,需要先了解下两个概念:“硬件线程”和“软件线程”。1. 硬件线程 相信大家手头的电脑都是双核以上的,像我这样古董的电脑都是双核的,这样的双核叫做物理内核。硬件线程又叫做逻辑内核,我们可以在”任务管理器“中查看”性能“标签页,如下图,我们知道有2个硬件线程。一般情况下,一个物理内核对应一个逻辑内核,比如我这里的2对2。当然如果你的cpu采用的是超线程技术,那么可能就会有4个物理内核对应8个硬件线程,现在有很多服务器都有8个硬件线程,上午在公司的服务器上截了个图...阅读全文
posted @ 2012-04-02 02:10 一线码农 阅读(2597) 评论(30) 编辑
摘要: 说到多线程,不可不说线程池,C#中关于池的概念很多,今天来整理下ThreadPool的使用。 是的,如果你很懒,如果你的执行任务比较短,如果你不想对线程做更精细的控制,那么把这些繁琐的东西丢给线程池吧。一:ThreadPool好了,下面看看TheadPool下有哪些常用的方法。1:GetMaxThreads,GetMinThreads 首先我们肯定好奇线程池到底给我们如何控制线程数,下面就具体的看一看。 1 class Program 2 { 3 static void Main(string[] args) 4 { 5 in...阅读全文
posted @ 2012-03-18 20:30 一线码农 阅读(3670) 评论(8) 编辑