系统级程序设计第五课内容——Linux线程管理
前言
本节课主要学习了在Linux系统下对线程的一系列操作,理解了线程与进程的差别,学习在线程的管理上与进程不同的地方。
一、创建线程
Linux系统中创建线程的系统调用接口为 pthread_create(),该函数存在于函数库pthread.h中,具体形式如下:
int pthread_create(
pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void *),
void *arg );
参数说明:
thread:一个传入传出参数,待创建线程的id指针;
attr:设置待创建线程的属性,通常传入NULL;
start_routine:一个函数指针,指向一个参数为void *,返回值也为void *的函数,该函数为待创建线程的执行函数;
arg:传给线程执行函数的参数。
线程调用pthread_create函数创建新线程后,当前线程会从pthread_create函数返回并继续向下执行,新线程执行函数指针start_routine所指的函数。
该函数创建成功返回0,创建失败返回errno。与进程不同的地方,接收错误信息时,需要先自定义变量接收errno,再调用strerror()将获取的错误代码转换成错误信息。
注意:线程id的类型pthread_t并非是一个正整数,只在当前进程中保证唯一。
代码举例如下:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
void *tfn(void *arg) {
printf("tfn--pid=%d,tid=%lu\n", getpid(), pthread_self());
return (void*)0;
}//of tfn
int main() {
pthread_t tempTid;
printf("main--pid=%d, tid=%lu\n", getpid(), pthread_self());
int tempRet = pthread_create(&tempTid, NULL, tfn, NULL);
if (tempRet != 0){
fprintf(stderr, "pthread_create error.");
exit(1);
}//of if
sleep(1);
return 0;
}//of main
二、测试进程与线程的区别
1、进程必须独立占用一段地址,而统一进程中的不同线程占用同一段地址。
2、全局变量在进程中不是共享的,但在线程中是共享的。
代码举例如下:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
int globVar = 100;
void *tfn(void *arg) {
globVar = 200;
printf("thread\n");
return NULL;
}//of tfn
int main(void) {
printf("At first var = %d\n", globVar);
pthread_t tempTid;
pthread_create(&tempTid, NULL, tfn, NULL);
sleep(1);
printf("after pthread_create, var = %d\n", globVar);
printf("------finished-------");
return 0;
}//of main
三、线程退出
线程中提供了一个用于单个线程退出的函数——pthread_exit ,位于函数库pthread.h中,具体形式如下:
void pthread_exit(void *retval);//参数retval表示线程的退出状态,通常设置为NULL。
之前学到的两种退出返回函数,return用于退出函数,exit用于退出进程,这两个都不能用来退出线程。
代码举例如下:分别用pthread_exit, return, exit使其中一个线程退出,观察其它线程的执行状况。
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void *tfn(void *paraArg) {
long int i;
i = (long int)paraArg;
if (i == 2) {
pthread_exit(NULL);
}//of if
sleep(i);
printf("I'm %ld th thread, Thread_ID = %lu\n", i + 1, pthread_self());
return NULL;
}//of tfn
int main(int paraArgc, char *paraArgv[]) {
long int tempNum = 5, i;
pthread_t tempTid;
if (paraArgc == 2) {
tempNum = atoi(paraArgv[1]);
}//of if
for (i = 0; i < tempNum; i++) {
pthread_create(&tempTid, NULL, tfn, (void *)i);
}//of for i
sleep(tempNum);
printf("I am main, I'm a thread! main_thread_ID = %lu\n", pthread_self());
return 0;
}//of main
四、线程终止
在线程操作中有一个与终止进程的函数kill()对应的系统调用函数pthread_cancel,该函数通过向制定线程发送CANCEL信号,使一个线程强行杀死另外一个线程。该函数位于函数库pthread.h中,具体形式如下:
int pthread_cancel(pthread_t thread);//参数thread为线程id。
与进程不同的是,调用该函数杀死线程时,需要等待线程到达某个取消点,线程才会成功被终止;
取消点通常伴随阻塞出现,用户也可以在程序中通过调用pthread_testcancel函数创造取消点;
pthread_exit使线程主动退出,pthread_cancel通过信号使线程被动退出;
注意:由于线程机制出现之前信号机制已经出现,信号机制在创建时并未考虑线程,线程与信号机制的兼容性略有不足,因此多线程编程时尽量避免使用信号,以免出现难以调试的错误。
代码举例如下:
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void *tfn(void *paraArg) {
while(1) {
printf("child thread ...\n");
pthread_testcancel();
}//of while
}//of tfn
int main(void){
pthread_t tempTid;
void *tempTret = NULL;
pthread_create(&tempTid, NULL, tfn, NULL);
sleep(1);
pthread_cancel(tempTid);
pthread_join(tempTid, &tempTret);
printf("child thread exit code = %ld\n", (long int)tempTret);
return 0;
}//of main
五、线程挂起
在进程中,可以使用wait(),waitpid()将进程挂起,以等待某个子进程结束,在线程中采用pthread_join函数,该函数位于函数库pthread.h中,具体形式如下:
int pthread_join(pthread_t thread, void **retval);
参数:
thread:表示被等待的线程id;
retval:用于接收thread线程执行函数的返回值指针,该指针的值与thread线程的终止方式有关:
– 通过return返回:retval存放的是thread线程函数的返回值;
– 其它线程通过系统调用pthread_cancel异常终止,retval存放的是常量PTHREAD_CANCELED;
– 自调用pthread_exit终止,retval存放的是pthread_exit的参数ret_val;
– 若不关心它的终止状态,retval设置为NULL。
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
typedef struct {
int a;
int b;
} exit_t;
void *tfn(void *paraArg){
exit_t *tempRet;
tempRet = malloc(sizeof(exit_t));
tempRet->a = 100;
tempRet->b = 300;
pthread_exit((void *)tempRet);
return NULL;
}//of tfn
int main(void){
pthread_t tempTid;
exit_t *tempRetval;
pthread_create(&tempTid, NULL, tfn, NULL);
pthread_join(tempTid, (void **)&tempRetval);
printf("a = %d, b = %d\n", tempRetval->a, tempRetval->b);
return 0;
}//of main
总结
本次课程主要学习了对于线程的管理,第一次认识到线程这一概念是在操作系统中,但都是概念性的知识,本节课中具体使用代码对一个线程进行创建、终止、退出、挂起等操作,对于线程的认识更加深刻,也进一步理解了线程与进程直接的区别。
参考链接:https://blog.csdn.net/search_129_hr/article/details/124646059