文章目录
1. 概念
在一个程序中,独立运行的程序片段叫做线程,利用它编程的概念就叫做多线程处理。一个程序至少有一个进程,一个进程至少有一个线程,如果把进程比作火车,那么一个线程就是这列火车的一节车厢。
典型的UNIX进程可以看成只有一个控制线程:一个进程在同一时刻只做一件事情。有了多个控制线程后,在程序设计师可以把进程设计成在同一时刻能够做不止一件事,每个线程处理各自独立的任务。
线程包含了表示进程内执行环境必须的信息:进程中标识线程的线程ID、一组寄存器值、栈、调度优先级和策略、信号屏蔽字、errno变量、线程私有数据。进程的所有信息对该进程的所有线程都是共享的,包括可执行的程序文本、程序的全部内存和堆内存、栈以及文件描述符。
2. 线程标识
就像每个进程一个也有ID一样,每个线程也有一个线程ID。进程ID在整个系统中是唯一的,但线程ID不同,线程ID只在它所属的进程环境中有效。
函数 pthread_self
功能
:获得自身的线程ID
函数原型
:
#include <pthread.h>
pthread_t pthread_self(void);
// 返回调用线程的线程ID
函数 pthread_equal
功能
:比较两个线程ID是否相等
函数原型
:
#include <pthread.h>
int pthread_equal(pthread_t tid1, pthread_t tid2);
// 若相等则返回非零值,否则返回0
3. 线程创建
!!!函数 pthread_create
功能
:创建一个新的线程
函数原型
:
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
// 成功返回0,否则返回错误编号
参数说明
:
thread:thread 用来返回该线程的线程ID。由 tidp 指向的内存单元被设置为新创建线程的线程ID。
attr:定制不同的线程的属性。(设为NULL则创建默认属性的进程)
start_routine:start_routine 是一个函数指针,其指向的函数就是所创建的子线程要执行的任务(函数)。 start_routine指向的函数只有一个无类型指针参数arg(void *func(void *arg)),如果需要向该函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg参数传入。
arg:传给所调用函数的参数(就是上一个参数里讲到的arg)。
4. 线程终止
如果进程中的任一线程调用了exit,_Exit 或者 _exit,那么整个进程就会终止。与此类似,如果信号的默认动作是终止进程,那么,把该信号发送到线程会终止整个进程。
单个线程可以通过下列三种方式退出,在不终止整个进程的情况下停止它的控制流。
- 线程只是从启动例程中返回,返回值时线程的退出码。
- 线程可以被统一进程中的其他线程取消。
- 线程调用 pthread_exit。
函数 pthread_exit
功能
:终止调用线程
函数原型
:
#include <pthread.h>
void pthread_exit(void *retval);
函数 pthread_join
功能
:与终止的线程连接
函数原型
:
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
// 成功则返回0,否则返回错误编号
函数 pthread_cancel
功能
:请求取消同一进程中的其他线程
函数原型
:
#include <pthread.h>
int pthread_cancel(pthread_t thread);
// 成功则返回0,否则返回错误编号
函数 pthread_cleanup_push 和 pthread_cleanup_pop
功能
:线程清理处理程序(pthread cleanup handler)
函数原型
:
#include <pthread.h>
void pthread_cleanup_push(void (*routine)(void *),void *arg);
void pthread_cleanup_pop(int execute);
当线程执行以下动作是调用清理函数(调用参数为arg,清理函数 routine 的调用顺序是由 pthread_cleanup_push 函数来安排的):
- 调用 pthread_exit 时。
- 响应取消请求时。
- 用非零 execute 参数调用 pthread_cleanup_pop 时。
5. 例子
一个例子:pthread_self() & pthread_create()
打印线程ID(printTID.c)
#include <pthread.h>
#include <stdio.h>
#include <error.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
pthread_t ntid;
void printtids(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)
{
printtids("new thread: ");
return 0;
}
int main(int argc, char **argv)
{
int err;
err = pthread_create(&ntid, NULL, thr_fn, NULL);
if(err != 0)
{
printf("cant't create thread: %s\n", strerror(err));
return -1;
}
printtids("main thread: ");
sleep(1);
return 0;
}
运行结果
:
直接 gcc printTID.c 时会报错:
cxx@raspberrypi:~/za/aboutThread $ gcc printTID.c
/tmp/ccIRYKIh.o: In function `main’:printTID.c:(.text+0x94): undefined reference to `pthread_create’
collect2: error: ld returned 1 exit status
原因是pthread不是linux下的默认的库,也就是在链接的时候,无法找到phread库中函数的入口地址,于是链接会失败。所以,在gcc编译的时候,要加 -pthread 参数。
cxx@raspberrypi:~/za/aboutThread $ gcc printTID.c -pthread
cxx@raspberrypi:~/za/aboutThread $ ./a.out
main thread: pid 18370 tid 1996349440 (0x76fde000)
new thread: pid 18370 tid 1994519664 (0x76e1f470)
两个例子:pthread_join() & pthread_create()
获得线程退出状态(threadExit.c)
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
void *thr_fn1(void *arg)
{
printf("thread 1 running!\n");
return ((void *)1);
}
void *thr_fn2(void *arg)
{
printf("thread 2 running!\n");
return ((void *)2);
}
int main(int argc, char **argv)
{
pthread_t tid1, tid2;
void *tret;
if(pthread_create(&tid1, NULL, thr_fn1, NULL) != 0)
{
printf("create thread 1 failed: %s\n", strerror(errno));
return -1;
}
if(pthread_create(&tid2, NULL, thr_fn2, NULL) != 0)
{
printf("create thread 2 failed: %s\n", strerror(errno));
运行结果
:
cxx@raspberrypi:~/za/aboutThread $ gcc threadExit.c -pthread
cxx@raspberrypi:~/za/aboutThread $ ./a.out
thread 1 running!
thread 2 running!
thread 1 exit code: 1
thread 2 exit code: 2