对于系统调用和库函数,这里统称为函数,毕竟对于上层使用它们来说,无需关心它们的细节。
1. 什么是线程一个进程在同一时刻只做一件事,引入了多线程之后,则在单进程环境中可同时执行多个任务,即
每个线程完成各自的任务,这样显得进程在同一时刻可以做多件事情。
线程的引入可以简化程序结构,在一个线程阻塞的时候可以执行其他的线程,提高了cpu的利用效
率,改善了程序的响应时间和吞吐量。
线程所包含的信息包括:
线程ID
一组寄存器值
栈
调度优先级和策略
信号屏蔽字
errno变量
线程私有数据
...
进程的所有信息对其中的线程都是共享的,如可执行程序文本、程序的全局内存和堆内存、栈、文
件描述符。
程序中可使用
#ifdef _POSIX_THREADS
来测试系统是否支持线程。
线程带来的好处:
o 为不同的事件分配单独的线程,简化处理异步事件的代码
o 同一进程中的多个线程可以简单的共享资源,而进程间的内存和文件描述符的共享需要
使用操作系统提供的复杂机制。
o 将一个问题分解为多个线程来处理,每个线程处理其中的一个任务,改善整个程序的吞
吐量。
o 交互的程序通过使用多线程来改善响应时间,如将程序中的用户输入输出部分与处理部
分分开。
2. 线程标识每个线程都有一个线程ID,这个线程ID只在该线程所属的进程环境中有效,其数据类型为
pthread_t
这样就将线程ID的类型抽象出来,至于具体的实现,pthread_t可以实现为一个结构类型,也可实
现为一个非负整数。在我的系统中,/usr/include/bits/pthreadtypes.h文件中可以看到
typedef unsigned long int pthread_t;说明在Linux中用一个非负整数来表示线程ID。
由于可移植性的要求,无法预知pthread_t类型的实现(它是一个整数还是一个结构体,或是其他),
所以无法直接来对两个线程的ID进行比较。就像无法直接对两个字符串直接进行比较一样(需要使用
strcmp函数),也需要调用一个函数来完成线程ID的比较
#include
int pthread_equal(pthread_t tid1, pthread_t tid2);
若相等则返回非0,否则返回0
一个线程获得其自身的线程ID
#include
pthread_t pthread_self(void);
返回线程ID
3. 创建线程
进程的创建使用fork函数,线程的创建使用pthread_create函数。
#include
int pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void *),
void *restrict arg
);
(其中restrict是C99中定义的一个关键字)
该函数会在进程中创建一个新线程,函数的各参数描述如下:
o 第一个参数thread是一个指向pthread_t对象的指针,当线程成功创建后,新线程
的ID会存储在thread指向的pthread_t对象中。
o 第二个参数attr,指定新建线程的属性。如果attr为NULL,则创建线程时使用默认的属
性。线程创建完成之后仍可使用相关函数来改变线程的属性,
o 第三个参数start_routine是一个函数指针(即函数的首地址),它是新建线程的入口,新
创建的线程从start_routine这个地址开始运行。从上面的定义来看,该函数指针所指向
的函数有两个特征:
函数返回值为 void * 类型
该函数有一个类型为 void * 类型的参数
o 第四个参数arg。start_routine所指向的函数有一个类型为 void * 的参数,即为这里
的 void *arg
返回值:
如果线程创建成功,则pthread_create函数返回0;否则返回一个非0值,该非0值是代
表一个错误码来指示出错类型。
返回值的错误码有如下几种:
o EAGAIN 不能获取(gain)到新线程所需的资源,或进程中的线程数量已达到了所允许的
最大值(PTHREAD_THREADS_MAX)
o EINVAL 创建时指定的属性attr无效(invalid)
o EPERM 线程的创建者没有相应的权限
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
pthread_t ntid;
void
err_sys(const char *s)
{
printf("%s\n", s);
exit(1);
}
void
printids(const char *s)
{
pid_t pid;
pthread_t tid;
pid = getpid();
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;
err = pthread_create(&ntid, NULL, thr_fn, NULL);
if (err != 0)
err_sys("pthread_create error\n");
printids("main thread:");
sleep(1);
exit(0);
}
|
编译
gcc -Wall test.c -lpthread
不要忘记链接线程库
./a.out (执行结果如下)
new thread: pid 30381 tid 3085904784 (0xb7ef2b90)
main thread: pid 30381 tid 3085907648 (0xb7ef36c0)
疑问:
观察程序执行结果,与apue2上面所描述的Linux下执行结果有两点差别:
o 这里两个线程的pid相同(说明是同一个进程)
而书中所叙述的是:Linux使用clone系统调用来实现pthread_create,clone用于创建
子进程。按书所说,主线程和新线程的pid应该不同(书上的结果确实是不同pid)。
o Linux里pthread_t实现为unsigned long int,但这里打印出来的线程ID值看起来十六
进制的表示更有意义些,这是为何?
Linux 线程模型的比较:LinuxThreads 和 NPTL
阅读(5518) | 评论(1) | 转发(0) |