Linux多线程相关事例
来源:互联网 发布:菅野洋子抄袭 知乎 编辑:程序博客网 时间:2024/05/18 01:48
一.基本功能
1.最简单例子--创建线程
/*** 创建线程**/#include <stdio.h>#include <stdlib.h>#include <pthread.h>void *thread1(void *arg){ printf("this thread1!\n");}int main(int argc,char **argv){ int ret; pthread_t tid; ret = pthread_create(&tid,NULL,thread1,NULL); sleep(4); return 0;}
2.传递简单参数
/*** 线程传递参数**/#include <stdio.h>#include <stdlib.h>#include <pthread.h>void *thread1(void *arg){ char *prm=NULL; prm=(char*)arg; printf("arg=%s\n",prm);}int main(int argc,char **argv){ int ret; pthread_t tid; ret = pthread_create(&tid,NULL,thread1,(void*)argv[1]); sleep(4); return 0;}
3.传递结构体参数
/*** 线程传递结构体**/#include <stdio.h>#include <stdlib.h>#include <pthread.h>typedef struct prm{ int in; char ch[255]; int *p;}oz;void *thread1(void *arg){ oz *prm2 = NULL; prm2=(oz*)arg; printf(" prm:in=%d\n prm:ch=%s\n prm:p=%d\n",prm2->in,prm2->ch,*(prm2->p));}int main(int argc,char **argv){ int ret; pthread_t tid; oz *prm1=malloc(sizeof(oz)); prm1->in=3; sprintf(prm1->ch,"hello world!"); prm1->p=malloc(sizeof(prm1->p)); *(prm1->p)=123456; ret = pthread_create(&tid,NULL,thread1,(void*)prm1); sleep(4); return 0;}
4.主线程等待子线程结束
/*** 主线程等待子线程结束**/#include <stdio.h>#include <stdlib.h>#include <pthread.h>void *thread1(void *arg){ char *prm=NULL; prm=(char*)arg; printf("arg=%s\n",prm); sleep(5);}int main(int argc,char **argv){ int ret; pthread_t tid; ret = pthread_create(&tid,NULL,thread1,(void*)argv[1]); pthread_join(tid,NULL); return 0;}
5.获取线程id
/*** 获取线程id**/#include <stdio.h>#include <stdlib.h>#include <pthread.h>void *thread1(void *arg){ char *prm=NULL; prm=(char*)arg; printf("thread1's id=%lu\n",pthread_self()); sleep(5);}int main(int argc,char **argv){ int ret; pthread_t tid; ret = pthread_create(&tid,NULL,thread1,(void*)argv[1]); printf("main thread's id=%lu\n",pthread_self()); printf("child thread's id=%lu\n",tid); pthread_join(tid,NULL); return 0;}
6.子线程结束释放资源
/*** 子线程结束释放资源**/#include <stdio.h>#include <stdlib.h>#include <pthread.h>void *thread1(void *arg){ char *prm=NULL; prm=(char*)arg; sleep(5); pthread_detach(pthread_self());}int main(int argc,char **argv){ int ret; pthread_t tid; ret = pthread_create(&tid,NULL,thread1,(void*)argv[1]); pthread_join(tid,NULL); return 0;}
百度百科:pthread_detach
创建一个线程默认的状态是joinable, 如果一个线程结束运行但没有被join,则它的状态类似于进程中的Zombie Process,即还有一部分资源没有被回收(退出状态码),所以创建线程者应该pthread_join来等待线程运行结束,并可得到线程的退出代码,回收其资源(类似于wait,waitpid)但是调用pthread_join(pthread_id)后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我们并不希望如此,比如在Web服务器中当主线程为每个新来的链接创建一个子线程进行处理的时候,主线程并不希望因为调用pthread_join而阻塞(因为还要继续处理之后到来的链接),这时可以在子线程中加入代码pthread_detach(pthread_self())或者父线程调用pthread_detach(thread_id)(非阻塞,可立即返回)这将该子线程的状态设置为detached,则该线程运行结束后会自动释放所有资源。
7.创建多个子线程;子线程退出;发送退出信号给子线程
/*** 创建多个子线程;子线程退出;发送退出信号给子线程**/#include <stdio.h>#include <stdlib.h>#include <pthread.h>void *thread2(void *arg){ char *prm=NULL; int i=5; prm=(char*)arg; while(1){ printf("thread2:%s\n",prm); sleep(1); }}void *thread1(void *arg){ char *prm=NULL; int i=5; prm=(char*)arg; while(i--){ printf("thread1:%s\n",prm); sleep(1); }
pthread_exit("omg!");pthread_detach(pthread_self());
}
int main(int argc,char **argv)
{
int ret;
pthread_t tid1,tid2;
void *join_ret;
ret = pthread_create(&tid1,NULL,thread1,(void*)argv[1]);
pthread_join(tid1,&join_ret);
printf("thread1 exit return:%s\n",(char *)join_ret);
ret = pthread_create(&tid2,NULL,thread2,(void*)argv[2]);
sleep(5);
if(!pthread_cancel(tid2))
printf("cancel pthread2\n");
pthread_join(tid2,NULL);
return 0;
}
8.一些错误的判断及处理
/*** 错误的判断及处理**/#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <errno.h>void *thread2(void *arg){ char *prm=NULL; int i=5; prm=(char*)arg; while(1){ printf("thread2:%s\n",prm); sleep(1); }}void *thread1(void *arg){ char *prm=NULL; int i=5; prm=(char*)arg; while(i--){ printf("thread1:%s\n",prm); sleep(1); } if(pthread_detach(pthread_self())!=0) exit(1); pthread_exit("omg!");}int main(int argc,char **argv){ int ret; pthread_t tid1,tid2; void *join_ret; ret = pthread_create(&tid1,NULL,thread1,(void*)argv[1]); if(ret!=0){ printf("can't create thread1: %s\n", (char *)strerror(ret)); return -1; } if((ret=pthread_join(tid1,&join_ret))!=0){ printf("pthread_join error %s\n",(char *)strerror(ret)); return ret; } printf("thread1 exit return:%s\n",(char *)join_ret); ret = pthread_create(&tid2,NULL,thread2,(void*)argv[2]); if(ret!=0){ printf("can't create thread2: error num:%d\n", errno); return errno; } sleep(5); if(!pthread_cancel(tid2)){ printf("cancel pthread2\n"); } else pthread_join(tid2,NULL); return 0;}
9.主线程发送信号给子线程
/*** 主线程发送信号给子线程**/#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <signal.h>void kill_handler(int i){ printf("kill_handler\n");}void *thread1(void *arg){ char *prm=NULL; prm=(char*)arg; printf("arg=%s\n",prm); while(1) ;}int main(int argc,char **argv){ int ret; pthread_t tid; ret = pthread_create(&tid,NULL,thread1,(void*)argv[1]); signal(SIGQUIT,kill_handler); sleep(5); if(pthread_kill(tid,SIGQUIT)==0) printf("signal to pthread!\n"); while(1) ; return 0;}
10.pthread_cleanup_push/pop
/*** pthread_cleanup_push/pop**/#include <stdio.h>#include <stdlib.h>#include <pthread.h>void cleanup(void *arg){ char* prm=NULL; prm=(char*)arg; printf("cleanup arg=%s\n",prm);}void *thread1(void *arg){ char *prm=NULL; prm=(char*)arg; pthread_cleanup_push(cleanup,(void*)prm); printf("arg=%s\n",prm); pthread_cleanup_pop(1);}int main(int argc,char **argv){ int ret; pthread_t tid; ret = pthread_create(&tid,NULL,thread1,(void*)argv[1]); pthread_join(tid,NULL); return 0;}
二.线程同步
第1部分 线程属性
1.detachstate
/*** detachstate**/#include <stdio.h>#include <stdlib.h>#include <pthread.h>void *thread1(void *arg){ char *prm=NULL; prm=(char*)arg; printf("arg=%s\n",prm); sleep(5);}int main(int argc,char **argv){ int ret; pthread_t tid; pthread_attr_t attr; int detachstate; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE); ret = pthread_create(&tid,&attr,thread1,(void*)argv[1]); if(ret!=0){ printf("pthread_create fail!\n"); return ret; } ret = pthread_attr_getdetachstate (&attr, &detachstate); if(ret!=0){ printf("pthread_attr_getdetachstate error!\n"); } if((detachstate==PTHREAD_CREATE_DETACHED)||(detachstate==PTHREAD_CREATE_JOINABLE)){ if(detachstate==PTHREAD_CREATE_DETACHED) printf("PTHREAD_CREATE_DETACHED\n"); else printf("PTHREAD_CREATE_JOINABLE\n"); } pthread_attr_destroy(&attr); pthread_join(tid,NULL); return 0;}
2.guardsize
/*** guardsize**/#include <stdio.h>#include <stdlib.h>#include <pthread.h>void *thread1(void *arg){ char *prm=NULL; prm=(char*)arg; printf("arg=%s\n",prm); sleep(5);}int main(int argc,char **argv){ int ret; pthread_t tid; pthread_attr_t attr; size_t guardsize; pthread_attr_init(&attr); pthread_attr_setguardsize(&attr,4093); ret = pthread_create(&tid,&attr,thread1,(void*)argv[1]); if(ret!=0){ printf("pthread_create fail!\n"); return ret; } ret = pthread_attr_getguardsize (&attr, &guardsize); if(ret!=0){ printf("pthread_attr_getguardsize error!\n"); } else{ printf("guardsize=%d\n",guardsize); } pthread_attr_destroy(&attr); pthread_join(tid,NULL); return 0;}
3.inheritsched
/*** inheritsched**/#include <stdio.h>#include <stdlib.h>#include <pthread.h>void *thread1(void *arg){ char *prm=NULL; prm=(char*)arg; int ret; pthread_attr_t attr; int inheritsched; pthread_attr_init(&attr); pthread_attr_setinheritsched(&attr,PTHREAD_EXPLICIT_SCHED); printf("arg=%s\n",prm); sleep(5); ret = pthread_attr_getinheritsched(&attr, &inheritsched); if(ret!=0){ printf("pthread_attr_getguardsize error!\n"); } if((inheritsched==PTHREAD_INHERIT_SCHED)||(inheritsched==PTHREAD_EXPLICIT_SCHED)){ if(inheritsched==PTHREAD_INHERIT_SCHED) printf("PTHREAD_INHERIT_SCHED\n"); if(inheritsched==PTHREAD_EXPLICIT_SCHED) printf("PTHREAD_EXPLICIT_SCHED\n"); } pthread_attr_destroy(&attr);}int main(int argc,char **argv){ int ret; pthread_t tid; ret = pthread_create(&tid,NULL,thread1,(void*)argv[1]); if(ret!=0){ printf("pthread_create fail!\n"); return ret; } pthread_join(tid,NULL); return 0;}
第2部分 mutex
1.mutex简单应用
//这个例子不好,可能无休止的运行,sum++的位置,及 sum 的判断条件,
使用trylock应该根据函数的返回值来决定是否进行sum的操作这才是正确的操作方式
/*** mutex简单应用**/#include <stdio.h>#include <stdlib.h>#include <pthread.h>pthread_mutex_t mutex;int sum=0;void *thread3(void *arg){ char *prm=NULL; prm=(char*)arg; sleep(2); pthread_mutex_trylock(&mutex); while(1){ sum++; if(sum==10) break; printf("thread3:%d\n",sum); sleep(1); } pthread_mutex_unlock(&mutex);}void *thread2(void *arg){ char *prm=NULL; prm=(char*)arg; sleep(1); pthread_mutex_lock(&mutex); while(1){ sum--; if(sum<0) break; printf("thread2:%d\n",sum); sleep(1); } pthread_mutex_unlock(&mutex);}void *thread1(void *arg){ char *prm=NULL; prm=(char*)arg; pthread_mutex_lock(&mutex); while(1){ sum++; if(sum==10) break; printf("thread1:%d\n",sum); sleep(1); } pthread_mutex_unlock(&mutex);}int main(int argc,char **argv){ int ret; pthread_t tid1,tid2,tid3; pthread_mutex_init(&mutex,NULL); ret = pthread_create(&tid1,NULL,thread1,(void*)argv[1]); ret = pthread_create(&tid2,NULL,thread2,(void*)argv[1]); ret = pthread_create(&tid3,NULL,thread2,(void*)argv[1]); pthread_join(tid1,NULL); pthread_join(tid2,NULL); pthread_join(tid3,NULL); pthread_mutex_destroy(&mutex); return 0;}
pthread_mutex_trylock/lock区别
本质区别不就在“try”吗
选哪个取决于场景:
当前线程锁失败,也可以继续其它任务,用trylock合适
当前线程只有锁成功后,才会做一些有意义的工作,那就_lock,没必要轮询trylock
2.进程mutex
//此例程中共享内存使用有些问题,下面的程序若将“尝试注释”代码部分注释,加锁不能堵塞。重新使用一个新的pthread_mutex_t变量,则能正常使用
/*** 进程mutex**/#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <sys/shm.h>#include <sys/types.h>#include <sys/ipc.h>typedef struct dev{ int i; pthread_mutex_t mutex;}DEV;int main(int argc,char **argv){ int ret; DEV *device=malloc(sizeof(DEV)); unsigned long j=0; pthread_mutexattr_t mutexattr; int shmid; shmid=shmget(123,sizeof(DEV*),IPC_CREAT|0660); shmctl(shmid,IPC_RMID,0); shmid=shmget(123,sizeof(DEV*),IPC_CREAT|0660); if(shmid==-1) perror("shmget()");
device=(DEV*)shmat(shmid,0,0);pthread_mutexattr_init(&mutexattr); pthread_mutexattr_setpshared(&mutexattr,PTHREAD_PROCESS_SHARED); pthread_mutex_init(&(device->mutex),&mutexattr); ret = fork(); if(ret==-1){ printf("fork error!\n"); } if (ret==0){//将此句放到进城创建之前device=(DEV*)shmat(shmid,0,0);printf("father!\n"); char buffer[1024]; while(1){ pthread_mutex_lock(&(device->mutex)); (device->i)--; printf("father : i = %d\n",(device->i)); //pthread_mutex_unlock(&(device->mutex));//尝试注释 sleep(1); } pthread_mutexattr_destroy(&mutexattr); pthread_mutex_destroy(&(device->mutex)); return 0; } else{ printf("child!\n"); //放到进城创建之前device=(DEV*)shmat(shmid,0,0); while(1){ pthread_mutex_lock(&(device->mutex)); (device->i)++; printf("child : i = %d\n",(device->i)); pthread_mutex_unlock(&(device->mutex)); sleep(1); }
pthread_mutexattr_destroy(&mutexattr); pthread_mutex_destroy(&(device->mutex));return 0; }}
//修改之后的程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/ipc.h>
typedef struct dev
{
int i;
pthread_mutex_t mutex;
}DEV;
int main(int argc,char *argv[])
{
int ret;
DEV *device = malloc(sizeof(DEV));
unsigned long j = 0;
pthread_mutexattr_t mutexattr;
pthread_mutex_t ev;
int shmid;
shmid = shmget(123,sizeof(DEV*),IPC_CREAT|0660);
shmctl(shmid,IPC_RMID,0);
shmid = shmget(123,sizeof(DEV*),IPC_CREAT|0660);
if(shmid == -1)
{
perror("shmget()");
}
device = (DEV*)shmat(shmid,NULL,0);
pthread_mutexattr_init(&mutexattr);
pthread_mutexattr_setpshared(&mutexattr,PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&(device->mutex),&mutexattr);
pthread_mutex_init(&ev,NULL);
ret = fork();
if(ret == -1)
{
printf("fork error\n");
}
if(ret == 0)
{
//father
printf("father\n");
char buffer[1024];
while(1)
{
//pthread_mutex_lock(&ev);
//printf("lock ev\n");
pthread_mutex_lock(&(device->mutex));
printf(" father lock share mutex\n");
//(device->i)--;
(device->i)++;
printf("father: i = %d\n",device->i);
pthread_mutex_unlock(&(device->mutex));
sleep(1);
}
printf("father while out\n");
pthread_mutexattr_destroy(&mutexattr);
pthread_mutex_destroy(&(device->mutex));
return 0;
}
else
{
sleep(5);
printf("child!\n");
device = (DEV*)shmat(shmid,0,0);
while(1)
{
pthread_mutex_lock(&(device->mutex));
(device->i)--;
printf("child: i = %d\n",device->i);
pthread_mutex_unlock(&(device->mutex));
sleep(1);
}
pthread_mutexattr_destroy(&mutexattr);
pthread_mutex_destroy(&(device->mutex));
return 0;
}
}
3.线程mutex
/*** 线程mutex**/#include <stdio.h>#include <stdlib.h>#include <pthread.h>int i=0;char buffer[1024];pthread_mutex_t mutex;void *thread2(void *arg){ while(1){ pthread_mutex_lock(&mutex); i++; printf("thread2 %d\n",i); if(fgets(buffer,1024,stdin)!=NULL) pthread_mutex_unlock(&mutex); sleep(1); }}void *thread1(void *arg){ while(1){ pthread_mutex_lock(&mutex); i--; printf("thread1 %d\n",i); if(fgets(buffer,1024,stdin)!=NULL) pthread_mutex_unlock(&mutex); sleep(1); }}int main(int argc,char **argv){ int ret; pthread_t tid1,tid2; pthread_mutexattr_t mutexattr; pthread_mutexattr_init(&mutexattr); pthread_mutexattr_setpshared(&mutexattr,PTHREAD_PROCESS_PRIVATE); pthread_mutex_init(&mutex,&mutexattr); ret = pthread_create(&tid1,NULL,thread1,NULL); ret = pthread_create(&tid1,NULL,thread2,NULL); pthread_join(tid1,NULL); pthread_join(tid2,NULL); pthread_mutexattr_destroy(&mutexattr); pthread_mutex_destroy(&mutex); return 0;}
自己的程序
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
int i=0;
char buffer[1024];
pthread_mutex_t mutex;
void *thread2(void *arg)
{
while(1)
{
pthread_mutex_lock(&mutex);
i++;
printf("thread2:%d\n",i);
if(fgets(buffer,1024,stdin)!=NULL)
{
pthread_mutex_unlock(&mutex);
printf("thread2 unlock\n");
}
sleep(1);
}
}
void *thread1(void * arg)
{
while(1)
{
pthread_mutex_lock(&mutex);
i--;
printf("thread1:%d\n",i);
if(fgets(buffer,1024,stdin)!=NULL)
{
pthread_mutex_unlock(&mutex);
printf("thread1 unlock\n");
}
sleep(1);
}
}
int main(int argc,char *argv[])
{
int ret;
pthread_t tid1,tid2;
pthread_mutexattr_t mutexattr;
pthread_mutexattr_init(&mutexattr);
pthread_mutexattr_setpshared(&mutexattr,PTHREAD_PROCESS_PRIVATE);
pthread_mutex_init(&mutex,&mutexattr);
ret = pthread_create(&tid1,NULL, thread1,NULL);
ret = pthread_create(&tid2,NULL, thread2,NULL);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_mutexattr_destroy(&mutexattr);
pthread_mutex_destroy(&mutex);
return0;
}
第3部分 条件变量
1.线程+mutex+cond
/*** 线程+mutex+cond**/#include <stdio.h>#include <stdlib.h>#include <pthread.h>pthread_mutex_t mutex;pthread_cond_t cond;int i=0;void *thread2(void *arg){ while(i!=30){ pthread_mutex_lock(&mutex); i++; if(!(i%6)) pthread_cond_signal(&cond); printf("thread2 %d\n",i); pthread_mutex_unlock(&mutex); sleep(1); }}void *thread1(void *arg){ while(i!=30){ pthread_mutex_lock(&mutex); pthread_cond_wait(&cond,&mutex); printf("thread1-%d\n",i); pthread_mutex_unlock(&mutex); sleep(1); }}int main(int argc,char **argv){ int ret; pthread_t tid1,tid2; pthread_condattr_t attr; pthread_mutexattr_t mutexattr; pthread_mutexattr_init(&mutexattr); pthread_mutexattr_setpshared(&mutexattr,PTHREAD_PROCESS_PRIVATE); pthread_condattr_init(&attr); pthread_condattr_setpshared(&attr,PTHREAD_PROCESS_PRIVATE); pthread_mutex_init(&mutex,&mutexattr); pthread_cond_init(&cond,&attr); ret = pthread_create(&tid1,NULL,thread1,(void*)argv[1]); ret = pthread_create(&tid2,NULL,thread2,(void*)argv[1]); pthread_join(tid1,NULL); pthread_join(tid2,NULL); printf("all thread done!\n"); pthread_condattr_destroy(&attr); pthread_cond_destroy(&cond); pthread_mutexattr_destroy(&mutexattr); pthread_mutex_destroy(&mutex); return 0;}
线程+mutex+cond+互锁
//这个程序很烂,上锁和解锁不配对,逻辑混乱,非常容易造成死锁,全局变量i都没有设置,没有全局的结束变量。
关于cond参考之后的关于pthread_cond_wait和pthread_cond_signal博客文章,
/*** 线程+mutex+cond+互锁**/#include <stdio.h>#include <stdlib.h>#include <pthread.h>pthread_mutex_t mutex;pthread_cond_t cond;int i=0;char buffer[1024];void *thread2(void *arg){ while(i!=30){ pthread_mutex_lock(&mutex); if(fgets(buffer,1024,stdin)!=NULL){ if(buffer[0]!='2'){ pthread_cond_signal(&cond); printf("thread2 pthread_cond_signal\n"); pthread_mutex_unlock(&mutex); } } else{ printf("thread2 pthread_cond_wait\n"); pthread_cond_wait(&cond,&mutex); } printf("thread2 running\n"); sleep(1); }}void *thread1(void *arg){ while(i!=30){ pthread_mutex_lock(&mutex); if(fgets(buffer,1024,stdin)!=NULL){ if(buffer[0]!='1'){ pthread_cond_signal(&cond); printf("thread1 pthread_cond_signal\n"); pthread_mutex_unlock(&mutex); } } else{ printf("thread1 pthread_cond_wait\n"); pthread_cond_wait(&cond,&mutex); } printf("thread1 running\n"); sleep(1); }}int main(int argc,char **argv){ int ret; pthread_t tid1,tid2; pthread_condattr_t attr; pthread_mutexattr_t mutexattr; pthread_mutexattr_init(&mutexattr); pthread_mutexattr_setpshared(&mutexattr,PTHREAD_PROCESS_PRIVATE); pthread_condattr_init(&attr); pthread_condattr_setpshared(&attr,PTHREAD_PROCESS_PRIVATE); pthread_mutex_init(&mutex,&mutexattr); pthread_cond_init(&cond,&attr); ret = pthread_create(&tid1,NULL,thread1,(void*)argv[1]); ret = pthread_create(&tid2,NULL,thread2,(void*)argv[1]); pthread_join(tid1,NULL); pthread_join(tid2,NULL); printf("all thread done!\n"); pthread_condattr_destroy(&attr); pthread_cond_destroy(&cond); pthread_mutexattr_destroy(&mutexattr); pthread_mutex_destroy(&mutex); return 0;}
- Linux多线程相关事例
- 多线程事例
- 多线程缓存事例
- 一个java 多线程事例
- oracle job事例相关
- linux 多线程相关
- 函数指针和多线程事例
- 【ODPS】TableTunnel多线程下载事例
- 四个作用域相关事例
- linux 计划任务事例
- Linux Makefile 小事例
- linux多线程编程----相关概念
- linux多线程编程相关文章
- linux系统相关学习:多线程
- linux多线程编程相关知识
- linux多线程相关(东拼西凑)
- linux DHCP网络故障排除事例
- linux中select的事例
- php header()的用法总结
- ubuntu12.04 安装配置jdk1.7
- 文章标题
- The Log:每个程序员都应该知道有关实时数据的统一抽象(2) 数据集成
- Android Api Demos登顶之路(六十五)Graphics-->AlphaBitmap
- Linux多线程相关事例
- Java final关键字
- LeetCode Compare Version Numbers
- The Log:每个程序员都应该知道有关实时数据的统一抽象(3)日志与实时流处理
- Nginx基础. Nginx中内存地址对齐(转)
- C++监听文件夹下的添加、修改、删除文件事件
- 消息推送实现方法、移动终端及消息推送系统
- System.OutOfMemoryException
- How Many Tables(hdu1213)