Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1097291
  • 博文数量: 242
  • 博客积分: 10209
  • 博客等级: 上将
  • 技术积分: 3028
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-12 09:27
文章分类

全部博文(242)

文章存档

2014年(1)

2013年(1)

2010年(51)

2009年(65)

2008年(124)

我的朋友

分类: LINUX

2009-08-07 16:04:07

  线程池可以有效减少线程创建和销毁的的开销,同时简化编程操作。如果在项目中会频繁使用使用线程,一般会自己实现一个线程池,现有的比较好的线程池实现是ACE和glib。有时间的话看看这些线程池的具体实现,相信对linux线程的理解会上升一个档次。不过这次讲的主要是如何使用现有glib库在自己的程序中方便地使用多线程。
  我们这次的工作是先从服务器的一个目录下载文件fulllist.txt,该文件中每一行都是一个文件名,我们再从服务器的一个目录下载这些文件到本地。这些文件都是以http协议的格式下载的。由于文件有几千个,单进程下载太慢,所以使用多线程,就是想初步了解一下多线程的大致使用和glib库线程池的使用方法。

  例子代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include

#define SERV_IP "XXXXX"
#define SERV_PORT 80
#define MAX_FILE_PATH 256
#define LIST_FILE_PATH "xxxxxx"
#define XML_FILE_PATH  "xxxxxx"
#define MAX_THREAD_NUM  10

void getfile(gpointer path_file,gpointer data);

/* 用于调试记录实际下载文件个数*/
//pthread_mutex_t g_file_count_lock;
//int g_file_count=0;

pthread_mutex_t g_file_path_lock;

/* get file name from file path. */
/* etc. the file path is "/epg/videos/8.xml", so the name we get is 8.xml.*/
char * get_file_name(const char * file_path){
  char *ptr1,*ptr2;
  ptr1=file_path;
  while(*ptr1!='\0'){
    if(*ptr1=='/') ptr2=ptr1;
    ptr1++;
  }
  ptr2++;
  return ptr2;
}


int main(int argc,char **argv){
  FILE *fp=NULL;
  char *ptch;
  char file_path[MAX_FILE_PATH];
  char file_name[MAX_FILE_PATH];
  GThreadPool *thread_pool;
  int count=0;

  /* initialize the global mutex */
  pthread_mutex_init(&g_file_path_lock,NULL);

  getfile(LIST_FILE_PATH,NULL);
  fp=fopen("fulllist.txt","r");
  g_thread_init(NULL);
  thread_pool=g_thread_pool_new(getfile,NULL,MAX_THREAD_NUM,TRUE,NULL);
  if(thread_pool==NULL) {
    printf("g_thread_pool_new() error!\n");
    exit(0);
  }
     
while(fgets(file_name,MAX_FILE_PATH,fp)!=NULL){
    count++;

    /* get rid of the last '\n' or '\r' in file_name, */
    /* because the function fgets() do read it. */
    ptch=file_name;
    while(*ptch!='\n'&&*ptch!='\r')
       ptch++; 
    *ptch='\0';

    /* befor we change the path_file, we check if weather another is */
   /*  using it. */
    pthread_mutex_lock(&g_file_path_lock); 

    /* get the complete filepath */
    strcpy(file_path,XML_FILE_PATH);   
    strcat(file_path,file_name);
    strcat(file_path,".xml");
  
    g_thread_pool_push(thread_pool,(gpointer*)file_path,NULL);

  }
 
  g_thread_pool_free(thread_pool,0,1);
  fclose(fp);
 
  /* 用于调试,输出fulllist.txt中的文件数目和最后线程实际创建的文件数目,看是否相同 */
  //printf("real: %d   get: %d",count,g_file_count);

  return 0;

}

void getfile(gpointer file_path,gpointer user_data){
  int sockfd;
  char buffer[1024];
  struct sockaddr_in server_addr;
  int nbytes;

  /* we can't use the parameter file_path passed to this function,
  // because this is a function will be exec in many threads asy,
  // we use a mutex to protect the thread_shared varibale file_path,
  // we unlock it after we copy it to another variable locally defined.
  */
  char thread_file_path[MAX_FILE_PATH];
  char* file_name;
  FILE * fp;
  char request[1024];
  int send, totalsend;
  int i;

  strncpy(thread_file_path,file_path,MAX_FILE_PATH);
  pthread_mutex_unlock(&g_file_path_lock);

  file_name=get_file_name((char *)thread_file_path);
          

  /* 客户程序开始建立 sockfd描述符 */
  if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)/*建立SOCKET连接*/
  {
    fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));

    goto end;
  }

  /* 客户程序填充服务端的资料 */
  bzero(&server_addr,sizeof(server_addr));
  server_addr.sin_family=AF_INET;
  server_addr.sin_port=htons(SERV_PORT);
  inet_pton(AF_INET,SERV_IP,&server_addr.sin_addr);

  /* 客户程序发起连接请求 */
  if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)/*连接网站*/
  {
    fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));
    goto end;
  }

/* 构建符合格式的request */
  sprintf(request, "GET /%s HTTP/1.1\r\nAccept: */*\r\nAccept-Language: zh-cn\r\n\
User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)\r\n\
Host: %s:%d\r\nConnection: Close\r\n\r\n", (char *)thread_file_path, SERV_IP, SERV_PORT);
  printf("%s", request);/*准备request,将要发送给主机*/


  /*发送http请求request*/
  send = 0;totalsend = 0;
  nbytes=strlen(request);
  while(totalsend < nbytes) {
    send = write(sockfd, request + totalsend, nbytes - totalsend);
    if(send==-1)  {printf("send error!%s\n", strerror(errno));exit(0);}
    totalsend+=send;
    printf("%d bytes send OK!\n", totalsend);
  }
  fp = fopen(file_name, "a");
  if(!fp)  {
    printf("create file error! %s\n", strerror(errno));
    goto end;
  }
 
  /*
  //用于调试
  //pthread_mutex_lock(&g_file_count_lock);
  //g_file_count++;
  //pthread_mutex_unlock(&g_file_count_lock);
  */

  //printf("\nThe following is the response header:\n");
  i=0;
  /* 连接成功了,接收http响应,response */
  while((nbytes=read(sockfd,buffer,1))==1)
  {
    if(i < 4)  {
      if(buffer[0] == '\r' || buffer[0] == '\n')  i++;
      else i = 0;
      //printf("%c", buffer[0]);/*把http头信息打印在屏幕上*/
    }
    else  {
      fwrite(buffer, 1, 1, fp);/*将http主体信息写入文件*/
      i++;
      if(i%1024 == 0)  fflush(fp);/*每1K时存盘一次*/
    }
  }
  fclose(fp);
  /* 结束通讯 */
  close(sockfd);
  end:
   ;
}

注. 红色的是涉及使用glib线程池的代码,蓝色的是涉及使用互斥量控制对多线程共享变量访问的代码。
 

编写时碰到的问题:
1. 包含glib库,除了程序中使用#include , 编译选项要加上`pkg-config --cflags --libs glib-2.0` , 如果直接使用 -I/usr/include/glib2.0/glib/ 会报出很多glib.h包含文件的错误.

2. 使用g_thread_init要加上编译选项`pkg-config --cflags --libs gthread-2.0`.否则会出现错误"g_thread_supported() failed" 还有一堆形如 "g_thread_pool_push: assertion 'real' failed " 的错误.

3. 执行线程的函数出错或者结束时应该是不能调用exit、return、pthread_exit之类的函数,
   我在出错时使用goto 到达程序末尾。 这只是我个人观点,没找到具体文档,不知道对不对。

4. 错误:"GThread-Error:**The supplied thread function vector is invalid".
   原因是提供的线程执行函数不符合g_thread_pool_new()函数的参数要求。

   普通的pthread中线程函数原型要求是 void *(*func)(void),
   而glib线程池要求的线程函数原型是  void (*func)(gpointer userdata,gpointer data).
阅读(4923) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~