1. 问题提出

编写程序模拟 Allen 和 Luffy 爬楼,楼层是从 1-10 层。Allen 每上一层休息 10ms,Luffy 每上一层休息 5ms. (需要注意的是 Allen 和 Luffy 是一起爬楼的,而非某个人先爬楼,爬完后接着另一个人爬楼。)

对于上述问题,利用多进程编程可以很好的解答。我们可以在父进程中执行 Allen 的爬楼过程而在子进程中执行 Luffy 的爬楼过程。

但是!如果要求用单进程来实现它,要怎么办?如果在单进程中写这样的代码,你应该怎么去实现?(假设你还不知道多线程的概念。)

看起来是不是手足无措?

好了,如果真不让你用多线程,估计这个问题你也很难解决。不过,你要知道在 Linux 2.4 内核之前,操作系统是不提供线程支持的(请参考​​《Linux 中 pthread 线程库历史》 ​​),你只能使用 pthread 线程函数库来创建线程(用户级线程),这也就是说,再没有多线程库的情况下,也能解决此问题。所以,不使用多线程库来解决它的方案,暂且作为一个难题抛出来。

2. 解决思路

使用 pthread 线程库,利用多线程解决爬楼问题。

这里只要粗略了掌握创建线程的函数 pthread_create 就行,有关此函数的知识,后文将会给出详细的介绍。

typedef void *(*start_routine) (void *);

int pthread_create(pthread_t *thread, const

这里先大致介绍一下各个参数:

  • thread: 传出参数,用来存储线程 id(你可以类比进程 id),通常它是个整数。
  • attr: 传入参数,用来设置线程属性。本文实验,先传空指针。
  • th_fn: 线程入口函数。线程启动后,进入这个函数。
  • arg: 线程入口函数的参数。

该函数返回 0 表示成功。

3. 程序清单

  • 代码
// hellothread.c

#include <unistd.h>
#include <pthread.h>
#include <stdio.h>

// 线程入口函数
void* th_fn(void *arg) {
char* name = (char*)arg;
int i;

for (i = 1; i <= 10; ++i) {
printf("%s : %d\n", name, i);
// 判断是 Allen 还是 Luffy
if (strcmp("Allen", name) == 0)
usleep(1000*10); // 10 ms
else
usleep(1000*5); // 5ms
}

return NULL;
}


int main() {
int err;
pthread_t allen, luffy;
char *name1 = "Allen";
char *name2 = "Luffy";

// 创建 allen 线程。
err = pthread_create(&allen, NULL, th_fn, (void*)name1);
if (err != 0) {
perror("pthread_create");
return -1;
}
// 创建 luffy 线程
err = pthread_create(&luffy, NULL, th_fn, (void*)name2);
if (err != 0) {
perror("pthread_create");
return -1;
}

// 问题:如果没有此行,会发生什么?
sleep(3);

// 打印线程 id 号
printf("Allen id: %lx\n", allen);
printf("Luffy id: %lx\n", luffy);

return 0;
}
  • 编程和运行
// 注意需要链接 pthread 库
$ gcc hellothread.c -o hellothread -lpthread
$ ./hellothread
  • 运行结果


75-初识线程_linux


不出意外,我们能看到 Luffy 总是先到达 10 楼 ^_^. 另外,每次运行的结果可能都不一样。

4. 总结

  • 初步理解什么是多线程
  • 知道线程有自己的 id 号
  • 认识到多进程和多线程的不同
  • 线程被创建完成后,线程函数什么时候被执行,哪个线程先被调度是未知的。

练习1:使用多进程完成本文中的实验。
练习2:使用多线程完成本文中的实验。
练习3:删除多线程代码中 main 函数里的 sleep(3),观察效果。