Linux编程基础 6.1:线程操作

1 线程操作

创建线程
挂起线程
终止线程
其它操作

1.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函数来获取。

【案例1】使用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 ifsleep(1);return 0;
}//of main

因为pthread库不是Linux系统默认的库,需要在编译的时候添加选项-lpthread:

gcc pthread_cre.c -o pthread_cre -lpthread

执行结果如下:
在这里插入图片描述

进程和线程的联系

  • 当一个进程创建一个线程时,原有的进程就会变成线程,两个线程共用一段地址空间;
  • 对内核而言,线程和进程没有区别,CPU会为每个线程与进程分配时间片,通过进程控制块来调度不同的线程和进程。

进程和线程的区别

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

【案例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);return 0;
}//of main

执行结果如下:
在这里插入图片描述

1.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 ifsleep(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 iffor (i = 0; i < tempNum; i++) {//将i转换为指针,在tfn中再强转回整型pthread_create(&tempTid, NULL, tfn, (void *)i);}//of for isleep(tempNum);printf("I am main, I'm a thread!\n""main_thread_ID = %lu\n", pthread_self());return 0;
}//of main

执行结果如下:
在这里插入图片描述

1.3 线程中止

#include <pthread.h>int pthread_cancel(pthread_t thread);

功能

  • 向指定线程发送CANCEL信号,使一个线程强行杀死另外一个线程,类似于终止进程函数kill;
  • 与进程不同的是,调用该函数杀死线程时,需要等待线程到达某个取消点,线程才会成功被终止;
  • 取消点通常伴随阻塞出现,用户也可以在程序中通过调用pthread_testcancel函数创造取消点;
  • pthread_exit使线程主动退出,pthread_cancel通过信号使线程被动退出;
  • 注意:由于线程机制出现之前信号机制已经出现,信号机制在创建时并未考虑线程,线程与信号机制的兼容性略有不足,因此多线程编程时尽量避免使用信号,以免出现难以调试的错误。

参数说明

  • thread:线程id。

返回值说明

  • 成功:0;
  • 不成功:返回errno。

【案例 4】使用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 tfnint 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

执行结果如下:
在这里插入图片描述

1.4 线程挂起

若将案例3主函数的sleep行删除,程序的执行结果如下:

I am main, I'm a thread!
main_thread_ID = 140186242565888
  • 问题:其它线程并没有执行,为什么?
  • 原因分析:
    – 线程与进程不同,若作为程序入口的原线程退出,系统内部会调用exit函数,导致同一进程中的所有线程都退出。
  • 解决方案:
    – 使用sleep函数使原线程阻塞,保证新创建的线程顺利执行;
    – 使用线程挂起函数。
#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。

【案例 5】使用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 tfnint 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

分析:tfn函数既调用了pthread_exit函数,又设置了关键字return;若如下代码打印不为空,则说明线程通过pthread_exit函数退出。

printf("a = %d, b = %d\n", tempRetval->a, tempRetval->b);

执行结果如下:
在这里插入图片描述

【案例 6】使用pthread_join回收多个新线程,并使用pthread_exit获取每个线程的退出状态。
分析:进程中可以使用waitpid函数结合循环结构使原进程等待多个进程退出,线程中pthread_join同样可以与循环结构结合,等待多个线程退出。

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>long int globVar = 100;
void *tfn(void *paraArg){long int i;i = (long int)paraArg;sleep(i);if(i == 1) {globVar = 333;printf("var = %d\n", globVar);pthread_exit((void *)globVar);} else if(i == 3){globVar = 777;printf("I'm %dth pthread, pthread_id = %lu, var = %d\n", i + 1, pthread_self(), globVar);pthread_exit((void *)globVar);} else {printf("I'm %dth pthread, pthread_id = %lu, var = %d\n", i + 1, pthread_self(), globVar);pthread_exit((void *)globVar);}//of ifreturn NULL;
}//of tfnint main(void){pthread_t tempTid[5];long int i;int *tempRet[5];for(i = 0; i < 5; i ++){	//创建新线程pthread_create(&tempTid[i], NULL, tfn, (void *)i);}//of for ifor(i = 0; i < 5; i ++){	//回收新线程pthread_join(tempTid[i], (void **)&tempRet[i]);printf("---%d's ret = %d\n", i, (long int)tempRet[i]);}//of for iprintf("I'm main pthread tid = %lu\t var = %d\n", pthread_self(), globVar);pthread_exit(NULL);
}//of main

分析:原线程的退出之所以会导致其它线程退出,是因为原线程执行完毕后,main函数会隐式调用exit函数,而pthread_exit函数可以只使调用该函数的线程退出。若在原线程调用return之前调用pthread_exit,同样可以保证其它线程的正常运行。

执行结果如下:
在这里插入图片描述

1.5 线程分离

  • 在线程终止后,其它线程调用pthread_join函数获取该线程的终止状态前,该线程会一直保持终止状态,这种状态类似进程中的僵尸态;
  • 为避免处于终止状态的线程占用内存,线程机制中提供了pthread_detach函数,可在线程被创建后设置线程分离,被分离的线程在执行结束后将会自动释放,不再等待其它线程回收。
#include <pthread.h>int pthread_detach(pthread_t thread);

功能

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

参数说明

  • thread:待分离的线程id。

返回值说明

  • 成功:0;
  • 不成功:返回errno。

【案例 7】使用pthread_detach分离新线程,使新线程自动回收。

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
void *tfn(void *paraArg) {int tempNum = 5;while (n--) {printf("pthread tfn n = %d\n", tempNum);sleep(1);}//of whilereturn (void *)7;
}//of tfn
int main(void) {pthread_t tempTid;void *tempRet;pthread_create(&tempTid, NULL, tfn, NULL);pthread_detach(tempTid);				//分离新线程int tempRetvar = pthread_join(tempTid, (void **)&tempRet);if (tempRetvar != 0) {fprintf(stderr, "pthread_join error %s\n", strerror(tempRetvar));} else {printf("pthread exit with %ld\n", (long int)tempRet);}//of ifreturn 0;
}//of main

分析:线程分离后调用pthread_join会失败,因此会执行如下代码:

fprintf(stderr, "pthread_join error %s\n", strerror(tempRetvar));

执行结果如下:
在这里插入图片描述
知识扩展:

  • linux线程执行 pthread有两种状态:joinable和unjoinable,默认是joinable;
    – 通过pthread_attr_getdetachstate获取线程的状态;
    – 方法一:通过如下代码来设置为状态joinable 还是 unjoinable
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&thr, &attr, &thread_start, NULL);

– 方法二:在线程中调用 pthread_detach, 如:pthread_detach(pthread_self()),将状态改为unjoinable,确保资源的释放:

void threadFunc( void *ptr ){       pthread_detach(pthread_self());pthread_exit(0) ;
}//of threadFuncpthread_t tid;
int status = pthread_create(&tid, NULL, threadFunc, NULL);

– 方法三:外部主线程主动调用 pthread_detach(tid):

pthread_t tid;
int status = pthread_create(&tid, NULL, ThreadFunc, NULL);
thread_detach(tid);
  • 如果线程是joinable状态,当线程函数自己返回退出时或pthread_exit时都不会释放线程所占用堆栈和线程描述符(总计8K多);只有调用pthread_join之后这些资源才会被释放;
  • 若是unjoinable状态的线程,堆栈和线程描述符这些资源在线程函数退出时或pthread_exit时自动会被释放。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/507411.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

灯效控制器和rgb控制器_更具个性的RGB风扇,机箱里的魔术师,九州风神MF120GT开箱...

写在前面不知道在2020年还有多少人会自己装机&#xff0c;相信喜欢个性的DIY玩家还是会陆陆续续跳进这个不小的坑。装机的乐趣在哪里&#xff0c;除了几大硬件&#xff0c;显然个性的灯效才是装机的灵魂。在光污染的道路上&#xff0c;普通的光环&#xff0c;光面等风扇&#x…

Linux编程基础 6.2:线程同步

2 线程同步 线程同步中的“同步”与生活中大家认知的“同步”略有不同&#xff0c;“同”不指同时&#xff0c;其主旨在于协同步调&#xff0c;按预定的先后次序执行线程&#xff1b;之所以需要实现线程同步&#xff0c;是因为若不对线程的执行次序加以控制&#xff0c;可能会…

电脑开两个微信_电脑怎么登录两个微信

1/4下载并安装微信电脑客户端&#xff0c;保证这台电脑没有登陆微信2/4就像正常打开微信一样&#xff0c;不过不是双击&#xff0c;而是快速连点四次3/4我们可以看到有两个微信登陆界面4/4用两个不同的账号进行扫码登陆即可

Linux编程基础 7.1:套接字通信流程及编程接口

1 socket通信流程 2 socket编程接口 Linux系统中常用的socket网络编程接口有&#xff1a; socket()bind()listen()accept()connect()send()recv()close()其中connect()与send()为客户端专用接口&#xff1b;bind()、listen()、accept()及recv()为服务器端专用接口&#xff1b…

cad2016中选择全图字体怎么操作_cad教程分享CAD中如何删除顽固图层?

Autocad教程公众号&#xff0c;专注于cad教程、cad教程视频的分享&#xff0c;欢迎关注&#xff0c;下载你所需的教程资源&#xff01;如你还未关注&#xff0c;请点击文章标题下方蓝色字体的"Autocad教程"进行关注。cad教程分享-CAD中如何删除顽固图层&#xff1f;方…

div 隐藏_div的position属性

如果你想把div放到合适的位置&#xff0c;请看看这篇文章。<!-- div的position属性--><html><style>.red{height:100px;background:red;}.green{height:100px;background:green;position:relative;left:50px;top:50px;}.black{height:100px;background:black…

乔布斯在斯坦福大学演讲稿英文_西方大文豪最爱的10个英文单词,写尽人世间细腻情感!...

从小浸染在汉语中的我们&#xff0c;常被汉字的意象美震撼到&#xff0c;一字就是一世界。汉字有种无与伦比的美丽&#xff0c;寥寥数字就能营造“只可意会不可言传”的意境&#xff0c;很多人感慨英文就是一串拉丁字母&#xff0c;无法传递细腻的情感。比如很多人说像「缘分」…

web前端开发论文写作_外语论文文献引言格式—MLA Style

我们之前讨论了外语论文文献引用格式—APA Style和Chicago Style—芝加哥论文脚注引注格式&#xff0c;今天我们来介绍在Essay写作中如何使用APA格式引用文献。MLA格式是英文论文写作最常用的一种参考文献格式。很多留学小伙伴都觉得MLA引用格式很复杂&#xff0c;今天译然小编…

pmbok第七版_PMBOK第七版要来了!都有哪些变化?你准备好了么?

PMBOK第7版#PMP##职场##项目管理##战略##价值#2020年1月15日PMBOK 第7版的征求意见稿发布&#xff0c;并于2020年1月14日结束意见征集&#xff0c;预计于今年第四季度发布。基于我的项目组合管理(PfMP)&#xff0c;项目集管理(PgMP),项目管理(PMP)的培训和研究经验&#xff0c;…

简述python的特性_Python的特性概要

1、和C比较&#xff0c;Python是解释型的语言&#xff0c; 2、a>字节码特性 b>动态语义&#xff0c;即在赋值时才确定数据类型 c>缩进&#xff0c;点击tap键缩进四个空格&#xff0c;使用编译器是记得查看&#xff01;3、注意 写Python的时候首先要记得定义编码格式&a…

python自动化办公实例展示_python自动化办公?学这些就够用了

知乎上有人提问&#xff1a;用python进行办公自动化都需要学习什么知识呢&#xff1f;这可能是很多非IT职场人士面临的困惑&#xff0c;想把python用到工作中&#xff0c;却不知如何下手&#xff1f; python在自动化办公领域越来越受欢迎&#xff0c;批量处理简直是加班族的福音…

Linux编程基础 8.3:I/O多路转接服务器

1 简介 为进一步提升服务器效率&#xff0c;人们提出了一种被称为I/O多路转接的模型。其中“多路”指代连接到服务器的多个客户端程序&#xff0c;而“转接”则是指在服务器主线与各分支之间设置一个“岗位”&#xff0c;由该岗位实现监控多路连接中数据状态的功能&#xff0c…

测井储层参数预测+人工智能方法

1 问题描述 测井储层参数预测 地层泥质含量&#xff1b;地层孔隙度&#xff08;POR&#xff09;&#xff1b;含水饱和度&#xff08;SW&#xff09;&#xff1b;渗透率&#xff08;PERM&#xff09;。 输入&#xff1a;声波时差&#xff08;AC&#xff09;、补偿中子&#x…

windows 安装openssh服务端_Git神器| SourceTree安装使用教程

SourceTree 是 Windows 和Mac OS X 下免费的Git客户端管理工具。支持创建、克隆、提交、push、pull 和合并等操作。一、sourcetree的安装1. 下载sourcetree下载链接&#xff1a;Sourcetree | Free Git GUI for Mac and Windows2. 安装sourcetree点击安装&#xff0c;第一个创建…

中国大学生计算机设计大赛--软件应用与开发类--经验总结

1 大赛介绍 中国大学生计算机设计大赛是我国高校面向本科生最早的赛事之一&#xff0c;自2008年开赛至2019年&#xff0c;一直由教育部高校与计算机相关教指委等或独立或联合主办。此赛目前是全国普通高校大学生竞赛排行榜榜单赛事之一。 我们参加了很多届大赛&#xff0c;在2…

网络与系统安全1.1 中间人攻击

1 Diffie-Hellman密钥交换过程 2 中间人攻击过程

centos 安装jdk_centos7配置jdk

一、查看centos7是否有自带jdk查看是否安装过java rpm -qa | grep java 如果是centos 一般会自带两个openjdk rpm -e --nodeps 要卸载的包 (包通过上面的指令可以获取到)] 将显示java的包全都卸载 命令&#xff1a; rpm -e --nodeps 要卸载的包二、创建/opt/app目录,用于安装JD…

隐私计算 1 隐私计算的定义与背景

1 隐私计算的定义 隐私计算&#xff1a;在保证数据提供方不泄露原始数据的前提下&#xff0c;对数据进行分析计算的一系列技术&#xff0c;保障数据在流通和融合过程中“可用不可见”。 隐私计算涉及到的学科&#xff1a; 密码学&#xff1b;统计学&#xff1b;计算机体系结…

php object 对象不存在。增加对象_PHP核心

1、单例模式单例模式三要素&#xff1a;拥有一个构造函数&#xff0c;并且为private拥有一个静态成员变量来保持类的实例拥有一个访问这个实例的静态方法<?php //Instan.php 单例类class Instan{static public $instance null;private function __construct(){var_dump(…

数据库查询求小于_SQL学习笔记(二)简单查询

本篇主要学习如何通过简单查询获取想要的数据。从零学会SQL&#xff1a;简单查询​www.zhihu.com基本的查询语句从数据库中查找数据时要使用select子句&#xff0c;select子句是sql中使用最多、最基本的子句。子句是sql语句的组成要素&#xff0c;是以某一个关键字作为开始的语…