在进程模型建立很久之后,线程模型才被引入到UNIX系统之中,线程在上世纪60年代被提出,80年代才被广泛使用起来。 进程的所有信息对该进程的所有线程都是共享的,包括可执行的程序文本、程序全局内存和堆内存、栈以及文件描述符。因此该进程的所有线程能够访问同一存储区。与进程相同,线程也具有ID标识。但是线程ID只在它所属的进程内起作用。一个进程中的线程ID在另一个进程中无意义。当在一个进程中对多个线程进行操作时,线程ID引用相应的线程。
线程标识:
进程ID用pid_t数据类型表示,是一个非负数。线程ID则用pthread_t数据类型来表示(linux中用无符号长整形表示)。
线程比较:
- #include <pthread.h>
- int pthread_equal(pthread_t tid1,pthread_t tid2);
- 返回值:相等则返回非0值,否则返回0;
获取线程的ID号:- #include <pthread.h>
- pthread_t pthread_self(void);
- 返回值:调用线程的ID
创建线程:- #include <pthread.h>
- int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *),void *restrict arg);
- 返回值:若成功则返回0,否则返回错误编号;
参数说明:
tidp:当pthread_create成功返回时,由tidp指向内存单元被设置为新创建线程的线程ID;
attr:用于定制各种不同的线程属性,一般为NULL;
start_rtn:表示该线程执行的函数,也就是新创建的线程从start_rtn函数的地址开始运行。该函数只有一个无类型指针参数arg(不是无类型的指针需要强制转换为无类型指针)。
arg:为线程执行函数start_rtn所需的函数。如果start_rtn函数传递的参数不止一个,那么需要将这些参数放到一个结构中,然后把这个结构的地址作为arg参数传入。
例子:由于pthread不是系统默认库,故在编译的时候需要加上-lpthread参数;
- #include <stdio.h>
- #include <pthread.h>
- void *myThread1(void)
- {
- int i;
- for (i=0; i<3; i++)
- {
- printf("This is the 1st pthread,created by zieckey.\n");
- sleep(1);//Let this thread to sleep 1 second,and then continue to run
- }
- }
- void *myThread2(void)
- {
- int i;
- for (i=0; i<3; i++)
- {
- printf("This is the 2st pthread,created by zieckey.\n");
- sleep(1);
- }
- }
- int main()
- {
- int i=0, ret=0;
- pthread_t id1,id2;
-
- /*创建线程1*/
- ret = pthread_create(&id1, NULL, (void*)myThread1, NULL);
- if (ret)
- {
- printf("Create pthread error!\n");
- return 1;
- }
-
- /*创建线程2*/
- ret = pthread_create(&id2, NULL, (void*)myThread2, NULL);
- if (ret)
- {
- printf("Create pthread error!\n");
- return 1;
- }
-
- pthread_join(id1, NULL);
- pthread_join(id2, NULL);
-
- return 0;
- }
创建好了线程,但是线程和进程谁先运行呢?答案是进程先运行,在后面的例子中有讲解。从上面例子也可以看成,进程在创建了线程之后有个函数pthread_join()就是等待两个线程运行完毕,如果没有则进程会直接退出,造成线程不能运行。
线程终止
进程中的任意一线程调用了exit、_Exit或者_exit,那么整个进程就会终止。
单个线程可以通过以下三种方式退出,在不终止整个进程的情况下停止它的控制流。
(1)线程只是从启动例程中返回,返回值是线程的退出码。
(2)线程可以被同一进程中的其他线程取消。
(3)线程调用pthread_exit.
- #include <pthread.h>
- void pthread_exit(void *rval_ptr);
rval_ptr是一个无类型指针,与传递启动例程的单个参数类似。一般为NULL;其他线程调用pthread_jion函数可以访问到该指针。
- #include <pthread.h>
- int pthread_join(pthread_t thread, void **rval_ptr);
- 返回值:成功返回0,否则返回错误编号。
调用线程将一直阻塞,知道指定的线程调用pthread_exit、从启动例程中返回或者被取消。
线程可以通过调用pthread_cancel函数来请求取消同一进程中的其他线程
- #include <pthread.h>
- int pthread_cancel(pthread_t tid);
- 返回值:成功返回0,否则返回错误;
线程可以安排它退出时需要调用的函数,这样的函数称为线程清理处理程序,线程可以建立多个清理处理程序。将创建线程的例子改一下,就可以测试这个函数,让线程2取消线程1的操作。
- #include <stdio.h>
- #include <pthread.h>
- void *myThread1(void)
- {
- int i;
- for (i=0; i<3; i++)
- {
- printf("This is the 1st pthread,created by zieckey.\n");
- sleep(1);//Let this thread to sleep 1 second,and then continue to run
- }
- }
- void *myThread2(void *tid)
- {
- int i,rc;
- printf("tid = %lu\n",tid);
- rc = pthread_cancel((int )tid);
- if (rc == 0)
- {
- printf("cancel success!\n");
- }
- for (i=0; i<3; i++)
- {
- printf("This is the 2st pthread,created by zieckey.\n");
- sleep(1);
- }
- }
- int main()
- {
- int i=0, ret=0;
- pthread_t id1,id2;
-
- /*创建线程1*/
- ret = pthread_create(&id1, NULL, (void*)myThread1, NULL);
- if (ret)
- {
- printf("Create pthread error!\n");
- return 1;
- }
- else
- printf("id1 = %lu\n",id1);
- /*创建线程2*/
- ret = pthread_create(&id2, NULL, (void*)myThread2, (void *)(id1));
- if (ret)
- {
- printf("Create pthread error!\n");
- return 1;
- }
-
- pthread_join(id1, NULL);
- pthread_join(id2, NULL);
-
- return 0;
- }
这样线程1中的内容就不会被打印出来。
- #include <pthread.h>
- void pthread_cleanup_push(void(*rtn)(void *),void *arg);
- void pthread_cleanup_pop(int execute);
这是一组进栈出栈的操作,当线程执行以下动作时调用清理函数,调用参数arg,清理函数rtn的调用顺序是由pthread_cleanup_push函数来安排的。
(1)调用pthread_exit时
(2)响应取消请求时
(3)用非零execute参数调用pthread_clean_pop时。
如果参数execute设置为0,清理函数将不被调用。无论哪种情况,pthread_cleanup_pop都将删除上一次pthread_cleanup_push调用建立的清理处理程序。
- #include <stdio.h>
- #include <pthread.h>
- #include <unistd.h>
- void *clean(void *arg)
- {
- printf("cleanup :%s \n",(char *)arg);
- return (void *)0;
- }
- void *thr_fn1(void *arg)
- {
- printf("thread 1 start \n");
- pthread_cleanup_push( (void*)clean,"thread 1 first handler");
- pthread_cleanup_push( (void*)clean,"thread 1 second hadler");
- printf("thread 1 push complete \n");
- if(arg)
- {
- return((void *)1);
- }
- pthread_cleanup_pop(0);
- pthread_cleanup_pop(0);
- return (void *)1;
- }
- void *thr_fn2(void *arg)
- {
- printf("thread 2 start \n");
- pthread_cleanup_push( (void*)clean,"thread 2 first handler");
- pthread_cleanup_push( (void*)clean,"thread 2 second handler");
- printf("thread 2 push complete \n");
- if(arg)
- {
- pthread_exit((void *)2);
- }
- pthread_cleanup_pop(0);
- pthread_cleanup_pop(0);
- pthread_exit((void *)2);
- }
- int main(void)
- {
- int err;
- pthread_t tid1,tid2;
- void *tret;
- err=pthread_create(&tid1,NULL,thr_fn1,(void *)1);
- if(err!=0)
- {
- printf("error .... \n");
- return -1;
- }
- err=pthread_create(&tid2,NULL,thr_fn2,(void *)1);
- if(err!=0)
- {
- printf("error .... \n");
- return -1;
- }
- err=pthread_join(tid1,&tret);
- if(err!=0)
- {
- printf("error .... \n");
- return -1;
- }
- printf("thread 1 exit code %d \n",(int)tret);
- err=pthread_join(tid2,&tret);
- if(err!=0)
- {
- printf("error .... ");
- return -1;
- }
- printf("thread 2 exit code %d \n",(int)tret);
-
- return 1;
- }
进程和线程原语的比较:
进程原语 线程原语 描述
fork pthread_create 创建新的控制流
exit pthread_exit 从现有的控制流中退出
waitpid pthread_join 从控制流中得到退出状态
atexit pthread_cleanup_push 注册在退出控制流时调用的函数
getpid pthread_self 获取控制流的ID
abort pthread_cancel 请求控制流的非正常退出
上面例子程序参考国嵌培训资料。
阅读(1804) | 评论(0) | 转发(0) |