# 11 | 线程:如何让复杂的项目并行执行? 上一节我们讲了如何创建进程,这一节我们来看如何创建线程。 ## 为什么要有线程? 其实,对于任何一个进程来讲,即便我们没有主动去创建线程,进程也是默认有一个主线程的。线程是负责执行二进制指令的,它会根据项目执行计划书,一行一行执行下去。进程要比线程管的宽多了,除了执行指令之外,内存、文件系统等等都要它来管。 所以,**进程相当于一个项目,而线程就是为了完成项目需求,而建立的一个个开发任务**。默认情况下,你可以建一个大的任务,就是完成某某功能,然后交给一个人让它从头做到尾,这就是主线程。但是有时候,你发现任务是可以拆解的,如果相关性没有非常大前后关联关系,就可以并行执行。 例如,你接到了一个开发任务,要开发200个页面,最后组成一个网站。这时候你就可以拆分成20个任务,每个任务10个页面,并行开发。都开发完了,再做一次整合,这肯定比依次开发200个页面快多了。 ![](https://static001.geekbang.org/resource/image/48/9e/485ce8195d241c2a6930803286302e9e.jpg) 那我们能不能成立多个项目组实现并行开发呢?当然可以了,只不过这样做有两个比较麻烦的地方。 第一个麻烦是,立项。涉及的部门比较多,总是劳师动众。你本来想的是,只要能并行执行任务就可以,不需要把会议室都搞成独立的。另一个麻烦是,项目组是独立的,会议室是独立的,很多事情就不受你控制了,例如一旦有了两个项目组,就会有沟通问题。 所以,使用进程实现并行执行的问题也有两个。第一,创建进程占用资源太多;第二,进程之间的通信需要数据在不同的内存空间传来传去,无法共享。 除了希望任务能够并行执行,有的时候,你作为项目管理人员,肯定要管控风险,因此还会预留一部分人作为应急小分队,来处理紧急的事情。 例如,主线程正在一行一行执行二进制命令,突然收到一个通知,要做一点小事情,应该停下主线程来做么?太耽误事情了,应该创建一个单独的线程,单独处理这些事件。 另外,咱们希望自己的公司越来越有竞争力。要想实现远大的目标,我们不能把所有人力都用在接项目上,应该预留一些人力来做技术积累,比如开发一些各个项目都能用到的共享库、框架等等。 在Linux中,有时候我们希望将前台的任务和后台的任务分开。因为有些任务是需要马上返回结果的,例如你输入了一个字符,不可能五分钟再显示出来;而有些任务是可以默默执行的,例如将本机的数据同步到服务器上去,这个就没刚才那么着急。因此这样两个任务就应该在不同的线程处理,以保证互不耽误。 ## 如何创建线程? 看来多线程还是有很多好处的。接下来我们来看一下,如何使用线程来干一件大事。 假如说,现在我们有N个非常大的视频需要下载,一个个下载需要的时间太长了。按照刚才的思路,我们可以拆分成N个任务,分给N个线程各自去下载。 我们知道,进程的执行是需要项目执行计划书的,那线程是一个项目小组,这个小组也应该有自己的项目执行计划书,也就是一个函数。我们将要执行的子任务放在这个函数里面,比如上面的下载任务。 这个函数参数是void类型的指针,用于接收任何类型的参数。我们就可以将要下载的文件的文件名通过这个指针传给它。 为了方便,我将代码整段都贴在这里,这样你把下面的代码放在一个文件里面就能成功编译。 当然,这里我们不是真的下载这个文件,而仅仅打印日志,并生成一个一百以内的随机数,作为下载时间返回。这样,每个子任务干活的同时在喊:“我正在下载,终于下载完了,用了多少时间。” ``` #include #include #include #define NUM_OF_TASKS 5 void *downloadfile(void *filename) { printf("I am downloading the file %s!\n", (char *)filename); sleep(10); long downloadtime = rand()%100; printf("I finish downloading the file within %d minutes!\n", downloadtime); pthread_exit((void *)downloadtime); } int main(int argc, char *argv[]) { char files[NUM_OF_TASKS][20]={"file1.avi","file2.rmvb","file3.mp4","file4.wmv","file5.flv"}; pthread_t threads[NUM_OF_TASKS]; int rc; int t; int downloadtime; pthread_attr_t thread_attr; pthread_attr_init(&thread_attr); pthread_attr_setdetachstate(&thread_attr,PTHREAD_CREATE_JOINABLE); for(t=0;t #include #include #define NUM_OF_TASKS 5 int money_of_tom = 100; int money_of_jerry = 100; //第一次运行去掉下面这行 pthread_mutex_t g_money_lock; void *transfer(void *notused) { pthread_t tid = pthread_self(); printf("Thread %u is transfering money!\n", (unsigned int)tid); //第一次运行去掉下面这行 pthread_mutex_lock(&g_money_lock); sleep(rand()%10); money_of_tom+=10; sleep(rand()%10); money_of_jerry-=10; //第一次运行去掉下面这行 pthread_mutex_unlock(&g_money_lock); printf("Thread %u finish transfering money!\n", (unsigned int)tid); pthread_exit((void *)0); } int main(int argc, char *argv[]) { pthread_t threads[NUM_OF_TASKS]; int rc; int t; //第一次运行去掉下面这行 pthread_mutex_init(&g_money_lock, NULL); for(t=0;t #include #include #define NUM_OF_TASKS 3 #define MAX_TASK_QUEUE 11 char tasklist[MAX_TASK_QUEUE]="ABCDEFGHIJ"; int head = 0; int tail = 0; int quit = 0; pthread_mutex_t g_task_lock; pthread_cond_t g_task_cv; void *coder(void *notused) { pthread_t tid = pthread_self(); while(!quit){ pthread_mutex_lock(&g_task_lock); while(tail == head){ if(quit){ pthread_mutex_unlock(&g_task_lock); pthread_exit((void *)0); } printf("No task now! Thread %u is waiting!\n", (unsigned int)tid); pthread_cond_wait(&g_task_cv, &g_task_lock); printf("Have task now! Thread %u is grabing the task !\n", (unsigned int)tid); } char task = tasklist[head++]; pthread_mutex_unlock(&g_task_lock); printf("Thread %u has a task %c now!\n", (unsigned int)tid, task); sleep(5); printf("Thread %u finish the task %c!\n", (unsigned int)tid, task); } pthread_exit((void *)0); } int main(int argc, char *argv[]) { pthread_t threads[NUM_OF_TASKS]; int rc; int t; pthread_mutex_init(&g_task_lock, NULL); pthread_cond_init(&g_task_cv, NULL); for(t=0;t