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…

华为交换机ssh思科交换机_华为交换机SSH(stelnet)配置实例教程

Stelnet(安全telnet)登陆也成为shh(sercure shell,安全外壳)登陆-----------必要配置---------------1、先根据加密算法生成秘钥对,用于传输数据时加密保护&#xff0c;保存在交换机中但不保存在配置文件中[Huawei]rsa local-key-pair create或[Huawei]dsa local-key-pair crea…

Linux编程基础 6.2:线程同步

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

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

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

idea 查询项目代码行数_idea统计代码行数Statistic的步骤详解

idea统计代码行数Statistic的步骤详解idea统计代码行数可以用到插件&#xff1a;Statistic。步骤&#xff1a;File→Settings进入Plugins点击Marketplace搜索Statistic 安装蓝框标出的插件重启idea后就可以看到效果了(图是拿的别人的&#xff0c;基本就是这效果)如果没有下…

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;方…

python开启多个端口服务_python bottle使用多个端口(多个进程)提高并发

我的程序是用python结合bottle框架写的&#xff0c;但bottle自带wsgi原本只是单进程单线程运行模式(Bottle 默认运行在内置的 wsgiref 服务器上面。这个单线程的 HTTP 服务器在开发的时候特别有用&#xff0c;但其性能低下&#xff0c;在服务器负载不断增加的时候也许会是性能瓶…

Linux编程基础 7.2:服务器和客户端编程案例

1 网络字节序 大端模式&#xff1a;若将数据的高字节保存在内存的低地址&#xff0c;将数据的低字节保存在内存的高地址&#xff1b; 小端模式&#xff1a;若将数据的高字节保存在内存的高地址&#xff0c;将数据的低字节保存在内存的低地址。 网络数据流&#xff1a;大端模式…

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…

苹果笔记本python怎么换行_python怎么换行,我的换行就是执行啊

展开全部首先运行终端或者cmd命令行(windows下)。e69da5e887aa62616964757a686964616f31333433646338执行python3.5的命令。然后输入如下图所示的内容。这种换行方法也可以在编辑器中进行&#xff0c;这里以vim为例&#xff0c;输入与上图类似的代码&#xff0c;保存为t.py脚本…

Linux编程基础 7.3:套接字本地通信

1 socket本地通信 socket原本是为网络通讯设计的&#xff0c;但后来在socket框架的基础上发展出了一种IPC&#xff08;进程通信&#xff09;机制&#xff0c;即UNIX Domain Socket&#xff0c;专门用来实现使用socket实现的本地进程通信。 本地通信的流程与使用的接口与基于TC…

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

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

dorado 刷新_dorado7常用内容

tabControl").set("currentTab","tab2");// 通过tab的索引(index)属性切换view.get("#tabControl").set("currentIndex",1);//根据名字切换tabvar tab self.get("currentTab").get("name");if(tab!"ta…

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

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

Linux编程基础--目录

本系列教程以《Linux编程基础》为基础&#xff0c;希望在大家的帮助下&#xff0c;逐步完善Linux下系统级程序设计。 1 初识Linux 1.1 课程简介及Linux介绍 2 Linux文件操作 2.1 文件I/O 2.2 文件操作 3 进程管理 3.1 进程控制 3.2 exec家族 3.3 进程同步 4 信号 4.1 系…

100999凑整到万位进一_四年级数学专项练习

填空题1、当除数是34时&#xff0c;试商时可以把除数看作( )&#xff0c;这样初商容易偏( )。2、()个26相加的和是468&#xff1b;()比12个15多20。3、34&#xff1d;21……&#xff0c;余数最大是()&#xff0c;这时被除数是()。4、在括号里填上合适的数。480秒()分540厘米()分…

cosx等价无穷小_第一章 函数与极限 第七节 无穷小的比较

我的公众号“每日晴天”&#xff0c;可关注领取我的笔记pdf版哦~------------------------------------------------------------------------------本文主要首先把书上的定义和知识点总结起来&#xff0c;方便复习要点背诵&#xff0c;同时在最后分割线之后补充自己当时在学习…

Linux编程基础 8.1:多进程并发服务器

多进程并发服务器 多线程并发服务器 I/O多路转接服务器 epoll的工作模式 1 多进程并发服务器 在多进程并发服务器中&#xff0c;若有用户请求到达&#xff0c;服务器将会调用fork()函数&#xff0c;创建一个子进程&#xff0c;之后父进程将继续调用accept()&#xff0c;而子进…

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

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