Linux编程基础 :线程操作1

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档



提示:以下是本篇文章正文内容,下面案例可供参考

一、了解线程

基本概念:
线程是一条执行路径,是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位,一个进程可以由很多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。同样多线程也可以实现并发操作,每个请求分配一个线程来处理。

进程和线程的联系:

  • 当一个进程创建一个线程时,原有的进程就会变成线程,两个线程共用一段地址空间;
  • 对内核而言,线程和进程没有区别,CPU会为每个线程与进程分配时间片,通过进程控制块来调度不同的线程和进程。
  • 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程是操作系统可识别的最小执行和调度单位。

进程和线程的区别:

  • 进程拥有独立的地址空间,当使用fork函数创建新进程时,若其中一个进程要对fork之前的数据进行修改,进程会根据“写时复制”原则,先复制一份该数据到子进程的地址空间,再修改数据。即便是全局变量,在进程间也不是共享的。
  • 线程间共享地址空间,一个线程对全局取的数据进行了修改,其它线程访问到的也是修改后的数据。

二、线程操作

1.创建线程

#include <pthread.h>
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 <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:%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 <pthread.h>
void pthread_exit(void *retval);

为什么要用pthread_exit:
return:用于退出函数,使函数返回函数调用处;
exit:用于退出进程,若在线程中调用该函数,那么该线程所处的进程也会退出。
功能:退出线程。

参数说明:
retval:表示线程的退出状态,通常设置为NULL。

【案例 3】 一个进程中创建4个新线程,分别用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);					//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 <pthread.h>

int pthread_cancel(pthread_t thread);

功能:

  • 向指定线程发送CANCEL信号,使一个线程强行杀死另外一个线程,类似于终止进程函数kill;
  • 与进程不同的是,调用该函数杀死线程时,需要等待线程到达某个取消点,线程才会成功被终止;
  • 取消点通常伴随阻塞出现,用户也可以在程序中通过调用pthread_testcancel函数创造取消点;
  • pthread_exit使线程主动退出,pthread_cancel通过信号使线程被动退出;

注意:
由于线程机制出现之前信号机制已经出现,信号机制在创建时并未考虑线程,线程与信号机制的兼容性略有不足,因此多线程编程时尽量避免使用信号,以免出现难以调试的错误。

参数说明:
thread:线程id。

返回值说明:
成功:0;
不成功:返回errno。

4 线程挂起

线程与进程不同,若作为程序入口的原线程退出,系统内部会调用exit函数,导致同一进程中的所有线程都退出。

#include <pthread.h>
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 <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_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 <pthread.h>
int pthread_detach(pthread_t thread);

功能:
将线程从主控线程分离,这样当线程结束后,它的退出状态不需要由其它线程来获取,而是由该线程自身自动释放;
pthread_join不能终止已处于detach状态的线程,若对于分离态的线程调用pthread_join,函数会调用失败并返回EINVAL。
参数说明:
thread:待分离的线程id。
返回值说明:
成功:0;
不成功:返回errno。

总结

线程操作结合进程操作相比较学习共同记忆

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值