由于同一进程的多个线程共享同一地址空间,因此Text Segment、Data Segment都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:
文件描述符表 每种信号的处理方式( SIG_IGN 、 SIG_DFL 或者自定义的信号处理函数) 当前工作目录 用户id和组id当然,线程也有起独特的组成部分:
线程id 上下文,包括各种寄存器的值、程序计数器和栈指针 栈空间 errno 变量 信号屏蔽字 调度优先级Linux上线程函数位于 libpthread 共享库中,因此在编译时要加上 -lpthread 选项,这点不要忘了。
线程创建:
#include <pthread.h> int pthread_create(pthread_t *restrict thread,<span style="white-space:pre"> </span>const pthread_attr_t *restrict attr, void *(*start_routine)(void*), void *restrict arg);返回值:成功返回0,失败返回错误号
在一个线程中调用pthread_create()创建新的线程后,当前线程从pthread_create()返回继续往下执行,而新的线程所执行的代码由我们传给 pthread_create 的函数指针 start_routine 决定。 start_routine 函数接收一个参数,是通过 pthread_create 的 arg 参数传递给它的,该参数的类型为 void * ,这个指针按什么类型解释由调用者自己定义。 start_routine 的返回值类型也是 void * ,这个指针的含义同样由调用者自己定义。 start_routine 返回时,这个线程就退出了,其它线程可以调用 pthread_join 得到 start_routine 的返回值,类似于父进程调用 wait(2) 得到子进程的退出状态。
注意thread_t实际上返回的是线程 的id,以前看过pid_t即进程id,其可以保证id在整个系统中是唯一的,但是线程id只能够保证在当前的进程中的id是唯一的。
下面看一个线程的小例子:
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<pthread.h> #include<unistd.h> pthread_t ntid; void printids(const char * s) { pid_t pid; pthread_t tid; pid = getpid(); tid = pthread_self(); //pthread_sel用于获取进程的tid 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(arg); return NULL; } int main() { int err = pthread_create(&ntid, NULL, thr_fn, "new thread; "); if(err != 0){ fprintf(stderr, "can't create thread: %s/n",strerror(err)); exit(1); } printids("main thread: "); sleep(1); return 0; }运行如下所示:
root@wc:~/Codes/Learn/learnThread# gcc thread.c -o thread -lpthread root@wc:~/Codes/Learn/learnThread# ./thread main thread: pid 5075 tid 563636032 (0x21986740) new thread; pid 5075 tid 555337472 (0x2119c700) root@wc:~/Codes/Learn/learnThread# vim thread.c上面的演示可见属于同一进程的多个线程调用 getpid(2) 可以得到相同的进程号,而调用 pthread_self(3) 得到的线程号各不相同。
如果任意一个线程调用了 exit 或 _exit ,则整个进程的所有线程都终止,由于从 main 函数 return 也相当于调用 exit ,为了防止新创建的线程还没有得到执行就终止,我们在 main 函数 return 之前延时1秒,这只是一种权宜之计,即使主线程等待1秒,内核也不一定会调度新创建的线程执行。
线程的终止:
1 从线程函数 return 。这种方法对主线程不适用,从 main 函数 return 相当于调用 exit 。
2 一个线程可以调用 pthread_cancel 终止同一进程中的另一个线程。
3 线程可以调用 pthread_exit 终止自己。
下面是pthread_exit以及pthread_join函数:
#include <pthread.h> void pthread_exit(void *value_ptr); #include <pthread.h> int pthread_join(pthread_t thread, void **value_ptr);对于pthread_join:调用该函数的线程将挂起等待,直到id为 thread 的线程终止。 thread 线程以不同的方法终止,通过 pthread_join 得到的终止状态是不同的:
一段演示代码如下所示:
#include<stdio.h> #include<stdlib.h> #include<pthread.h> #include<unistd.h> void * thr_fn1(void * arg) { printf("Thread 1 returning !\n"); return (void*)1; } void *thr_fn2(void * arg) { printf("Thread 2 returning !\n"); return (void*)2; } void *thr_fn3(void * arg) { while(1) { printf("thread 3 writing\n"); sleep(1); } } int main() { pthread_t tid; void * tret; pthread_create(&tid, NULL, thr_fn1, NULL); pthread_join(tid, &tret); printf("Thread 1 exit code is %d\n", (int)tret); pthread_create(&tid, NULL, thr_fn2, NULL); pthread_join(tid, &tret); printf("Thread 2 exit code is %d\n", (int)tret); pthread_create(&tid, NULL, thr_fn3, NULL); sleep(3); pthread_cancel(tid); pthread_join(tid, &tret); //进程在上一行就已经被结束了,所以下面的结束会失败返回-1 printf("thread 3 exit code %d\n", (int)tret); return 0; }下面是运行结果。
root@wc:~/Codes/Learn/learnThread# ./threadOver Thread 1 returning ! Thread 1 exit code is 1 Thread 2 returning ! Thread 2 exit code is 2 thread 3 writing thread 3 writing thread 3 writing thread 3 exit code -1
#include <pthread.h> int pthread_detach(pthread_t tid);