在文件夹中并行搜索关键字

   在文件夹中并行搜索关键字   

 

                                                                                          作者:  huzy    

 

  在阅读大型项目代码,经常要搜索某个标识符,找出标识符的声明、定义或引用的地方(指出在哪一行)。

本任务要求实现一个程序 idfind,其格式如下:

idfind [-j n] id dirname

选项 –j 指定并行线程数目。如果省略该选项,则只启动一个线程。

id 表示要查找的标识符,dirname 表示项目所在的目录。

 

要点分析:

我认为该任务需要的关键技术有以下的几点:

1. 文件目录递归,筛选出文件后缀名为 .h.cpp、 .c 的所有文件;

2. 多线程任务的分配;

3. 在文件中快速定位关键字。

 

我的主要思路:

用主线程去把任务压栈,子线程轮转操作,只要任务栈不空,子线程就不会停止工作。

 

任务轮转思想的示意图如下:

  先讲讲为什么我要采用栈来保存任务?

栈是后进先出的数据结构,所以也许你会质疑“为什么不用队列”?在这里因为我考虑到关键字的查找与任务的先后顺序无关,所以采用“栈”或“队列”的区别不大!

栈的数据共用一个进出口——栈顶,只需要加一把互斥锁。

 

我的方法:

任务压栈与出栈:

 1 int push_stack(stack &st, char filename[])
 2 
 3 {
 4 
 5   if(st.top == MAX-1)
 6 
 7   {
 8 
 9       printf("stack overflow !\n");
10 
11       return 0;
12 
13   }
14 
15   st.filename[++st.top] = (char*)malloc(strlen(filename)+1);
16 
17   strcpy(st.filename[st.top], filename);
18 
19   return 1;
20 
21 }
22 
23 int pop_stack(stack &st, char filename[])
24 
25 {
26 
27   pthread_mutex_lock(&mutex);            // 互斥锁
28 
29   if(st.top <= -1)
30 
31   {
32 
33       pthread_mutex_unlock(&mutex);      // 互斥锁
34 
35       return 0;
36 
37   }
38 
39   strcpy(filename, st.filename[st.top]);
40 
41   free(st.filename[st.top]);
42 
43   st.top --;
44 
45   pthread_mutex_unlock(&mutex);          // 互斥锁
46 
47   return 1;
48 
49 }

 

开始写程序时,我将创建线程放在判断栈空的后面,完成任务则退出。

后来发现存在一个很严重的效率问题:只要任务栈不空,就会使得“线程频繁的创建与退出”,这样的开销是非常大的,

所以改进后,我将创建线程提前,并且同时考虑“任务的规模”和“用户的需求”。

 

流程图如下:

                               

 

我的方法:

主函数:

int main(int argc,char *argv[])

{
  ……
  pthread_t *pth[PMAX];  // 线程指针数组
  ……

  //-------------------------------------------------------------------------

  init_stack(stk);          // 初始化栈, 【 global 】

  directory(path, stk);     // 遍历文件夹,并压栈文件全路径名

  KMP_next(key, strlen(key));    // 索引化关键字

  //--------------------------------------------------------------------------

  ts_num = stack_size(stk);
  if(th_num > ts_num)  // 如果指定的线程数目比任务还要多
      op_num = ts_num;
  else
      op_num = th_num;

  //--------------------------------------------------------------------------

  for(i=0; i<op_num; i++)                // 动态创建线程
  {
      pth[i] = (pthread_t *)malloc(sizeof(pthread_t));

      if(pthread_create(pth[i], NULL, thread_function, NULL))
      {
          printf("error creating thread.");
          abort();
      }
  }

  //--------------------------------------------------------------------------

  for(i=0; i<op_num; i++)

  {
      if(pthread_join(*pth[i],NULL))  // 等待所有线程结束
      {
          printf("error joining thread...\n");
          abort();
      }
  }

  //--------------------------------------------------------------------------

  // 释放内存
  destroy_stack(stk);
return 0; }

 

拓展:

  当然后来经过反复思量和与老师的探讨,我认为采用“队列”的效率会更高!

因为:队列的数据有两个口,一个进数据(队尾),一个出数据(队首),需要两把互斥锁,这就使得数据的“进入”与数据的“弹出”不会因为冲突而等待。

这样缩短了阻塞时间,使得并行效率更高。

 

  在我看来,任务分配的合理性只是提高效率的一部分。

因为该程序主要是针对“大型项目代码”的搜索,每当一个子线程完成一个任务,它就去“领取”下一个任务,为了避免同一个任务被不同的子线程重复“领取”,

在出栈的地方加上了互斥锁,然而每个代码文件的大小并不是很大,这就存在“频繁切换线程”的现象,也就是说子线程被阻塞的时间总和会随着线程数量的

增加而增加,而且每个线程的创建与回收需要较多的时间。

所以,我认为提高关键字定位的算法是至关重要的!

字符串定位的常规方法是采用暴力的“BF算法”,BF算法的优点是浅显易懂,但是它的最大效率制约问题是存在“主串指针的回溯”现象。

所以我采用的是“KMP算法”,KMP算法是对“BF算法”的改进,通过对关键字建立索引来避免“主串指针的回溯”。

 

完整代码:

  1 #include <pthread.h>
  2 #include <sys/types.h>
  3 #include <sys/stat.h>
  4 #include <unistd.h>
  5 #include <stdlib.h>
  6 #include <stdio.h>
  7 #include <string.h>
  8 #include <dirent.h>
  9 #include <malloc.h>
 10 
 11 #define MAX 100000       // 最多能处理文件个数
 12 #define PMAX 20          // 最多允许线程数目
 13 #define KEY_LEN  20      // 关键字的最大长度
 14 #define READ_LEN 256     // 每次读取一行的最大长度
 15 
 16 char key[KEY_LEN];       // 要查找的关键字
 17 int Next[KEY_LEN];       // kmp 索引
 18 pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER;  // 互斥
 19 
 20 
 21 //=============================================================================
 22 // 自定义的栈
 23 //=============================================================================
 24 typedef struct _stack    // 自定义栈
 25 {
 26     char *filename[MAX];
 27     int top;
 28 }stack;
 29 
 30 stack stk;    // 全局栈
 31 
 32 void init_stack(stack &st)  { st.top = -1; }
 33 
 34 int stack_is_empty(stack &st)  {  return st.top == -1 ? 1:0;  }
 35 
 36 int stack_size(stack &st)  {  return st.top+1;  }
 37 
 38 int push_stack(stack &st, char filename[])
 39 {
 40     if(st.top == MAX-1)
 41     {
 42         printf("stack overflow !\n");
 43         return 0;
 44     }
 45     st.filename[++st.top] = (char*)malloc(strlen(filename)+1);
 46     strcpy(st.filename[st.top], filename);
 47     return 1;
 48 }
 49 
 50 int pop_stack(stack &st, char filename[])
 51 {
 52     pthread_mutex_lock(&mutex); // 互斥锁
 53     if(st.top <= -1)
 54     {
 55         pthread_mutex_unlock(&mutex); // 互斥锁
 56         //printf("stack is empty ...\n");
 57         return 0;
 58     }
 59     strcpy(filename, st.filename[st.top]);
 60     free(st.filename[st.top]);
 61     st.top --;
 62     pthread_mutex_unlock(&mutex); // 互斥锁
 63     return 1;
 64 }
 65 
 66 void destroy_stack(stack &st)
 67 {
 68     int i;
 69     for(i=0; i<=st.top; i++)
 70         free(st.filename[i]);
 71     st.top = -1;
 72 }
 73 
 74 void print_stack(stack &st)
 75 {
 76     int i;
 77     for(i=0; i<=st.top; i++)
 78         printf("%s\n",st.filename[i]);
 79 }
 80 
 81 //=============================================================================
 82 // 目录递归遍历
 83 //=============================================================================
 84 /*判断文件是否是个目录文件,是目录返回 1 ,不是目录返回 0*/
 85 int testdir(char *path)
 86 {
 87     struct stat buf;
 88     if(lstat(path,&buf)<0)
 89     {
 90         return 0;
 91     }
 92     if(S_ISDIR(buf.st_mode))
 93     {
 94         return 1; //directory
 95     }
 96     return 0;
 97 }
 98 
 99 //=============================================================================
100 /*遍历目录*/
101 int directory(char *path, stack &st)
102 {
103     DIR *db;                   /*打开目录类型文件信息的 结构体*/
104     char filename[256];
105     struct dirent *dir_info;   /*目录类型文件属性信息的 结构体*/
106     int len;
107 
108     db=opendir(path);
109     if(db==NULL)
110     {
111         printf("Open dir error !\n");
112         return 0;
113     }
114     memset(filename,0,256);
115 
116     while ((dir_info=readdir(db)))
117     {
118         if((strcmp(dir_info->d_name,".")==0)
119                 ||(strcmp(dir_info->d_name,"..")==0))
120             continue;
121         else
122         {
123             sprintf(filename,"%s/%s",path,dir_info->d_name);
124             if(testdir(filename))
125                 directory(filename, st);   /* 递归 */
126             else
127             {
128                 len = strlen(filename);
129                 if(strcmp(&filename[len-2],".h") == 0              // .h文件
130                         || strcmp(&filename[len-2],".c") == 0      // .c文件
131                         || strcmp(&filename[len-4],".cpp") == 0 )  // .cpp文件
132                 {
133                     //printf("%s\n",filename);
134                     if(0 == push_stack(st, filename))
135                         return 0;   // 如果栈溢出
136                 }
137             }
138         }
139         memset(filename,0,256);
140     }
141     closedir(db);
142     return 0;
143 }
144 
145 //=============================================================================
146 // kmp 快速定位
147 //=============================================================================
148 // 索引化 t 串
149 
150 void KMP_next(char str[], int len)
151 {
152     int k=-1;
153     int j=0;
154     Next[0]=-1;
155     while(j<len-1)    //
156     {
157         if(k==-1 || str[k]==str[j])
158         {
159             k++;
160             j++;
161             Next[j]=k;
162         }
163         else
164             k=Next[k];
165     }
166 }
167 
168 //=============================================================================
169 // 在 s 串中快速定位 t 串
170 
171 int KMP_index(char s_str[], int s_len, char t_str[], int t_len)
172 {
173     int i,j,v;
174     i=0;
175     j=0;
176 
177     if(s_str[0] == '\n') // 空行
178         return -1;
179 
180     while(i<s_len && j<t_len)
181     {
182         if(j==-1 || s_str[i]==t_str[j])
183         {
184             j++;
185             i++;
186 
187             if(s_str[i] == '\n')
188                 break;
189         }
190         else
191             j=Next[j];
192     }
193     if(j>=t_len)
194         v=i-t_len+1;
195     else
196         v=-1;
197     return v;
198 }
199 
200 //=============================================================================
201 // 多线程创建以及处理
202 //=============================================================================
203 
204 void process(char filename[])
205 {
206     int key_len = strlen(key); // 关键字长度
207     int index;      // 关键字位置
208     int rw = 0;     //209 
210     // 查找 key 值
211     //printf("find key ...\n");
212 
213     FILE *fp = fopen(filename,"r");
214     if(!fp)  return;
215 
216     char buf[READ_LEN];
217     while ( fgets(buf, READ_LEN, fp) != NULL )
218     {
219         rw ++;
220         index = KMP_index(buf, READ_LEN, key, key_len);
221         if(index != -1)   // 找到
222         {
223             printf("%s --> row:%d\n", filename,rw);
224         }
225         //memset(buf, 0, READ_LEN);
226     }
227     fclose(fp);
228 }
229 
230 //=============================================================================
231 // 线程创建函数
232 void *thread_function(void *arg)
233 {
234     char filename[256];
235     //printf("> create thread .\n");
236 
237     while(1)
238     {
239         //pthread_mutex_lock(&mutex); // 互斥锁
240         if(0 == pop_stack(stk, filename))  // 栈空
241         {
242             //pthread_mutex_unlock(&mutex); // 互斥锁
243             break;
244         }
245         //else
246             //pthread_mutex_unlock(&mutex); // 互斥锁
247 
248         process(filename);   // 查找操作
249     }
250     return NULL;
251 }
252 
253 //=============================================================================
254 // main 函数
255 int main(int argc,char *argv[])
256 {
257     int i;
258     int th_num;             // 指定线程数
259     int ts_num;             // 任务数
260     int op_num;             // 实际线程数
261     char path[256];         // 要遍历的目录
262 
263     pthread_t  *pth[PMAX];  // 线程指针数组
264 
265     if(argc < 3 || argc > 4)
266     {
267         printf("参数错误!\n");
268         return 0;
269     }
270     else if(argc == 3)
271     {
272         th_num = 1;
273         strcpy(key, argv[1]);
274         strcpy(path, argv[2]);
275     }
276     else
277     {
278         sscanf(argv[1], "-%d", &th_num);
279         printf("%d\n",th_num);
280         strcpy(key, argv[2]);
281         strcpy(path, argv[3]);
282     }
283 
284     //------------------------------------------------------
285     //strcpy(path, "/home/huzy/Desktop"); // /linux011VC
286 
287     init_stack(stk);       // 初始化栈, 【 global 】
288     directory(path, stk);  // 遍历文件夹,并压栈文件全路径名
289     //print_stack(stk);    // 输出测试
290     //printf("file num: %d\n", stk.top);
291     //------------------------------------------------------
292     //strcpy(key, "main");          // 【 global 】
293 
294     KMP_next(key, strlen(key));     // 索引化关键字
295     //------------------------------------------------------
296     //th_num = 5;
297 
298     ts_num = stack_size(stk);
299     if(th_num > ts_num)   // 如果指定的线程数目比任务还要多
300         op_num = ts_num;
301     else
302         op_num = th_num;
303     //------------------------------------------------------
304     for(i=0; i<op_num; i++)             // 动态创建线程
305     {
306         pth[i] = (pthread_t *)malloc(sizeof(pthread_t));
307         if(pthread_create(pth[i], NULL, thread_function, NULL))
308         {
309             printf("error creating thread.");
310             abort();
311         }
312     }
313     //------------------------------------------------------
314     for(i=0; i<op_num; i++)
315     {
316         if(pthread_join(*pth[i],NULL))   // 等待所有线程结束
317         {
318             printf("error joining thread...\n");
319             abort();
320         }
321     }
322     //------------------------------------------------------
323     // 释放内存
324     destroy_stack(stk);
325     printf("end!\n");
326     //------------------------------------------------------
327     return 0;
328 }

 

 

posted @ 2012-05-28 12:24  hp+y  Views(2099)  Comments(0Edit  收藏  举报