提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 一、了解线程
- 二、线程操作
- 1.创建线程
- 2.线程退出
- 3.线程中止
- 4 线程挂起
- 5 线程分离
- 总结
提示:以下是本篇文章正文内容,下面案例可供参考
一、了解线程
基本概念:
线程是一条执行路径,是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位,一个进程可以由很多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。同样多线程也可以实现并发操作,每个请求分配一个线程来处理。
进程和线程的联系:
- 当一个进程创建一个线程时,原有的进程就会变成线程,两个线程共用一段地址空间;
- 对内核而言,线程和进程没有区别,CPU会为每个线程与进程分配时间片,通过进程控制块来调度不同的线程和进程。
- 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程是操作系统可识别的最小执行和调度单位。
进程和线程的区别:
- 进程拥有独立的地址空间,当使用fork函数创建新进程时,若其中一个进程要对fork之前的数据进行修改,进程会根据“写时复制”原则,先复制一份该数据到子进程的地址空间,再修改数据。即便是全局变量,在进程间也不是共享的。
- 线程间共享地址空间,一个线程对全局取的数据进行了修改,其它线程访问到的也是修改后的数据。
二、线程操作
1.创建线程
#include
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg);
**功能:**创建线程;线程调用pthread_create函数创建新线程后,当前线程会从pthread_create函数返回并继续向下执行,新线程执行函数指针start_routine所指的函数。
参数说明:
- thread:一个传入传出参数,待创建线程的id指针;
- attr:设置待创建线程的属性,通常传入NULL;
- start_routine:一个函数指针,指向一个参数为void *,返回值也为void *的函数,该函数为待创建线程的执行函数;
- arg:传给线程执行函数的参数。
返回值说明:
成功:返回0;
不成功:返回errno。
特别说明:
- 进程id的类型pid_t是一个正整数,在整个系统都是唯一的;
- 线程id的类型pthread_t并非是一个正整数,只在当前进程中保证唯一;
- 当前进程调用pthread_create后获得的thread为新线程id;
- 线程id不能简单地使用printf函数打印,而应使用pthread_self函数来获取。
案例:使用pthread_create函数创建线程,并使原线程与新线程分别打印自己的线程id。
#include
#include
#include
#include
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:%s\n", strerror(tempRet));
exit(1);
}//of if
sleep(1);
return 0;
}//of main
因为pthread库不是Linux系统默认的库,需要在编译的时候添加选项-lpthread:
gcc pthread_cre.c -o pthread_cre -lpthread
2.线程退出
#include
void pthread_exit(void *retval);
为什么要用pthread_exit:
return:用于退出函数,使函数返回函数调用处;
exit:用于退出进程,若在线程中调用该函数,那么该线程所处的进程也会退出。
功能:退出线程。
参数说明:
retval:表示线程的退出状态,通常设置为NULL。
【案例 3】 一个进程中创建4个新线程,分别用pthread_exit, return, exit使其中一个线程退出,观察其它线程的执行状况。
#include
#include
#include
#include
void *tfn(void *paraArg) {
long int i;
i = (long int)paraArg; //强转
if (i == 2) {
pthread_exit(NULL); //return, exit(0)
}//of if
sleep(i); //通过i来区别每个线程
printf("I'm %dth 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++) {
//将i转换为指针,在tfn中再强转回整型
pthread_create(&tempTid, NULL, tfn, (void *)i);
}//of for i
sleep(tempNum);
printf("I am main, I'm a thread!\n"
"main_thread_ID = %lu\n", pthread_self());
return 0;
}//of main
3.线程中止
#include
int pthread_cancel(pthread_t thread);
功能:
- 向指定线程发送CANCEL信号,使一个线程强行杀死另外一个线程,类似于终止进程函数kill;
- 与进程不同的是,调用该函数杀死线程时,需要等待线程到达某个取消点,线程才会成功被终止;
- 取消点通常伴随阻塞出现,用户也可以在程序中通过调用pthread_testcancel函数创造取消点;
- pthread_exit使线程主动退出,pthread_cancel通过信号使线程被动退出;
注意:
由于线程机制出现之前信号机制已经出现,信号机制在创建时并未考虑线程,线程与信号机制的兼容性略有不足,因此多线程编程时尽量避免使用信号,以免出现难以调试的错误。
参数说明:
thread:线程id。
返回值说明:
成功:0;
不成功:返回errno。
4 线程挂起
线程与进程不同,若作为程序入口的原线程退出,系统内部会调用exit函数,导致同一进程中的所有线程都退出。
#include
int pthread_join(pthread_t thread, void **retval);
功能:
- 挂起线程,等待指定线程thread结束;
- 类似于wait,waitpid将进程挂起,以等待某个子进程结束;
- 该函数中指定的线程必须与调用该函数的线程处于同一个进程中,且多个线程不能同时挂起等待同一个进程,否则pthread_join将会返回错误。
参数说明:
- thread:表示被等待的线程id;
- retval:用于接收thread线程执行函数的返回值指针,该指针的值与thread线程的终止方式有关:
– 通过return返回:retval存放的是thread线程函数的返回值;
– 其它线程通过系统调用pthread_cancel异常终止,retval存放的是常量PTHREAD_CANCELED;
– 自调用pthread_exit终止,retval存放的是pthread_exit的参数ret_val;
– 若不关心它的终止状态,retval设置为NULL。
返回值说明:
成功:0;
不成功:返回errno。
案例: 使用pthread_exit退出线程,为线程设置退出状态并将线程的退出状态输出。
#include
#include
#include
#include
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_creat(&tempTid, NULL, tfn, NULL);
//调用pthread_join可以获取线程的退出状态
pthread_join(tempTid, (void **)&tempRetval);
printf("a = %d, b = %d\n", tempRetval->a, tempRetval->b);
return 0;
}//of main
5 线程分离
- 在线程终止后,其它线程调用pthread_join函数获取该线程的终止状态前,该线程会一直保持终止状态,这种状态类似进程中的僵尸态;
- 为避免处于终止状态的线程占用内存,线程机制中提供了pthread_detach函数,可在线程被创建后设置线程分离,被分离的线程在执行结束后将会自动释放,不再等待其它线程回收。
#include
int pthread_detach(pthread_t thread);
功能:
将线程从主控线程分离,这样当线程结束后,它的退出状态不需要由其它线程来获取,而是由该线程自身自动释放;
pthread_join不能终止已处于detach状态的线程,若对于分离态的线程调用pthread_join,函数会调用失败并返回EINVAL。
参数说明:
thread:待分离的线程id。
返回值说明:
成功:0;
不成功:返回errno。
总结
线程操作结合进程操作相比较学习共同记忆