线程是一个进程中的控制和执行单元(unit), 传统的 UNIX 进程可以看成是只有一个控制线程: 在同一时刻,它只能做一件事。
通过在进程中创建多个线程, 可以并发地完成多个不同的任务。
进程的所有信息对该进程的所有线程都是共享的, 包括可执行的程序文本、程序的全局内存和堆内存、栈以及文件描述符。
多线程的用途和好处包括:
当然,多线程也带来了一些难题, 其中最重要的就是因为共享数据而带来的数据一致性问题, 以及线程之间的同步,等等。
Note
多线程和多处理器并没有直接关系, 即使使用的是单处理器,也可以从多线程中获益。
pthread_create
函数用于创建新线程:
#include <pthread.h>
// 创建成功返回 0 ,否则返回错误编号
int pthread_create(pthread_t *thread, // 保存新线程的 id
const pthread_attr_t *attr, // 定制线程的属性
void *(*start_routine) (void *), // 线程要执行的函数
void *arg); // 传给函数的参数
和创建进程类似, 系统并不保证新线程和原有线程之间那个会先执行, 所以线程程序一定要有相应的同步措施, 并且线程所执行的程序不能依赖于特定的执行顺序。
另外,
线程相关函数出错时,
通常以返回错误代码而不是设置 errno
的方式来报告错误,
这样更简洁一些。
pthread_self
函数用于返回线程的 id ,同一个进程的线程 id 是唯一的:
#include <pthread.h>
pthread_t pthread_self(void);
以下是书本提供的,创建新线程并打印线程 id 的示例代码:
// 11-1.c
#include "apue.h"
#include <pthread.h>
pthread_t ntid;
// 打印进程 id 和线程 id
void
printids(const char *s)
{
pid_t pid = getpid();
pthread_t tid = pthread_self();
printf("%s pid %u tid %u (0x%x)\n",
s, (unsigned int) pid, (unsigned int) tid, (unsigned int) tid);
}
// 新线程的执行函数
void *
thr_fn(void *arg)
{
printids("new thread: ");
return ((void *)0);
}
int
main(void)
{
int err = pthread_create(&ntid, NULL, thr_fn, NULL);
if (err != 0)
err_quit("can't create thread: %s\n", strerror(err));
printids("main thread:");
// 等待子线程完成
sleep(1);
exit(0);
}
以下是代码的执行结果(记得编译的时候要带上 -lpthread
):
$ ./11-1.out
main thread: pid 3729 tid 3075602112 (0xb751f6c0)
new thread: pid 3729 tid 3075599168 (0xb751eb40)
线程有以下两种外部/自然终止方式:
exit
、 _Exit
或者 _exit
,那么整个进程终止。还有以下三种线程主动终止的方式:
pthread_exit
。除非某个线程是进程的最后一个线程,
否则,
当一个线程终止时,
它所共享的进程资源不会被释放,
atexit
也不会被调用。
pthread_exit
的签名如下:
#include <pthread.h>
void pthread_exit(void *retval);
retval
用于向使用 pthread_join
等待这个线程的另一个线程返回值。
pthread_join
将调用线程阻塞,直到指定的线程终止为止,它的签名如下:
#include <pthread.h>
// 成功返回 0 ,否则返回错误代码
int pthread_join(pthread_t thread, void **retval);
thread
是等待的目标线程, retval
用于保存目标线程的返回值。
以下是书本提供的,测试线程退出的例程:
// 11-2.c
#include "apue.h"
#include <pthread.h>
void *
thr_fn1(void *arg)
{
printf("thread 1 returning\n");
return ((void *) 0);
}
void *
thr_fn2(void *arg)
{
printf("thread 2 exiting\n");
pthread_exit((void *) 2);
}
int
main(void)
{
int err;
pthread_t tid1, tid2;
void *tret;
// 创建线程 1
err = pthread_create(&tid1, NULL, thr_fn1, NULL);
if (err != 0)
err_quit("create thread 1 fail\n", strerror(err));
// 创建线程 2
err = pthread_create(&tid2, NULL, thr_fn2, NULL);
if (err != 0)
err_quit("create thread 2 fail\n", strerror(err));
// 等待线程 1 执行完毕
err = pthread_join(tid1, &tret);
if (err != 0)
err_quit("join thread 1 fail\n", strerror(err));
printf("thread 1 exit code %d\n", (int) tret);
// 等待线程 2 执行完毕
err = pthread_join(tid2, &tret);
if (err != 0)
err_quit("join thread 2 fail\n", strerror(err));
printf("thread 2 exit code %d\n", (int) tret);
exit(0);
}
执行结果:
$ ./11-2.out
thread 1 returning
thread 2 exiting
thread 1 exit code 0
thread 2 exit code 2