linux 线程基础
线程基础函数
查看进程中有多少个线程,查看线程的LWP
ps -Lf 进程ID(pid)
执行结果:LWP列
y:~$ ps -Lf 1887
UID PID PPID LWP C NLWP STIME TTY STAT TIME CMD
ys 1887 1341 1887 0 3 14:57 tty2 Sl 0:00 /usr/lib/ibus/ibus
ys 1887 1341 1889 0 3 14:57 tty2 Sl 0:00 /usr/lib/ibus/ibus
ys 1887 1341 1890 0 3 14:57 tty2 Sl 0:00 /usr/lib/ibus/ibus
线程共享的资源:
注意:信号和线程最好不要一起使用。又用信号又用多线程的架构不太合理。
- 文件描述符表
- 信号的处理方式
- 当前工作目录
- 用户ID和组ID
- 内存地址空间(.text/.data/.bss/heap/共享库,栈区(stack)不共享)
非线程共享的资源:
线程ID
处理器现场和栈指针(内核栈)
独立的栈空间(用户空间栈)
errno变量
所以不能用函数perrno打印错误信息了。
使用strerror函数打印错误信息
#include <string.h>
char *strerror(int errnum);
- errnum:errno
阻塞信号集合
调度优先级
现场优,缺点
- 优点:1,提高程序并发性。2,开销小。3,数据通信,共享方便
- 缺点:1,因为使用的时库函数,所以不太稳定。2,调试,编写困难。3,对信号支持不好。
进程和线程都是通过系统函数clone创建的。
每个线程有自己独立的PCB
pcb:进程控制块结构体:/usr/src/linux-headers-4.15.0-29/include/linux/sched.h
进程id:系统中每个进程有唯一的id,在c语言中用pid_t类型表示,是个非负整数。
进程状态:就绪,运行,挂起,停止等状态
描述虚拟地址空间的信息
描述控制终端的信息
进程执行时的当前工作目录(current working directory)
umask掩码
文件描述符表,包含很多指向file结构体的指针
和信号相关的信息
用户id和组id
会话(session)和进程组
进程可以使用的资源上限(Resource Limit)
用【ulimit -a】查看:
ys@ys:~$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 7743
max locked memory (kbytes, -l) 16384
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 7743
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
pthread_create函数:创建一个线程,并开始执行这个线程
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
- thread:线程的ID
- attr:线程的属性,一般不使用
- start_routine:函数指针
- arg:start_routine函数的参数
- 返回值:成功返回0;失败返回errno
编译的时候要加【-lpthread】
pthread_self函数:得到线程的id
#include <pthread.h>
pthread_t pthread_self(void);
例子:得到主线程的ID和子线程的ID。注意要sleep1秒,不睡的话,主线程就先结束了,所以子线程里的打印打不出来。
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
void * thr(void* args){
printf("in thread %d, %lu\n", getpid(), pthread_self());
}
int main(){
pthread_t tid;
pthread_create(&tid, NULL, thr, NULL);
printf("in main thread %d, %lu\n", getpid(), pthread_self());
sleep(1);
return 0;
}
pthread_exit函数:终止线程
#include <pthread.h>
void pthread_exit(void *retval);
- retval:线程的返回值
改进上面的例子,用pthread_exit代替sleep
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
void * thr(void* args){
printf("in thread %d, %lu\n", getpid(), pthread_self());
//exit(1);//不要在子线程里调用此函数,调用的结果是把整个进程终止了。
//return NULL;//可以使用
//pthread_exit(NULL);//可以使用
}
int main(){
pthread_t tid;
pthread_create(&tid, NULL, thr, NULL);
printf("in main thread %d, %lu\n", getpid(), pthread_self());
//sleep(1);
pthread_exit(NULL);
return 0;
}
终止线程时的注意事项
- 要使用pthread_exit函数,不能使用exit函数
- 也可以使用return,但是在主线程里使用return的话,就把进程终止了。
- exit是推出进程,所以不要在线程函数里调用exit函数
pthread_join函数:阻塞等待回收线程资源,其实也是等待线程结束,所以是阻塞的函数,线程不执行完,pthread_join就一直处于阻塞状态,类似wait函数(回收子进程的函数,也是阻塞的)。并且它的第二个参数是可以接收线程的返回值的。
线程不回收也会变成僵尸线程,线程里也有PCB资源,也要回收。
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
- thread:函数pthread_create调用后,传出的第一个参数的值,也就是线程的ID
- retval:二级指针,线程推出时,返回的信息。
- 返回值:成功返回0;失败返回errno
例子:实验pthread_join是否是阻塞。
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
void * thr(void* args){
printf("in thread %d, %lu\n", getpid(), pthread_self());
sleep(1);
return (void*)200;
}
int main(){
pthread_t tid;
pthread_create(&tid, NULL, thr, NULL);
printf("in main thread %d, %lu\n", getpid(), pthread_self());
void *ret;
pthread_join(tid, &ret);
printf("return value:%d\n", (int)ret);
return 0;
}
结果分析,发现在子线程睡的1秒内,pthread_join是阻塞的,线程的返回值200,也可以打印出来,但是编译有警告。
用pthread_exit函数也可以设置线程的返回值和return的效果一模一样。
pthread_exit((void*)11);
return (void*)11;
pthread_cancel函数:终止线程,并把线程的返回值设置为-1(是个宏)
#include <pthread.h>
int pthread_cancel(pthread_t thread);
- thread:线程ID
- 返回值:成功返回0;失败返回errno
例子:验证被函数pthread_cancel终止的进程的返回值。
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
void * thr(void* args){
while(1){
printf("in thread %d, %lu\n", getpid(), pthread_self());
sleep(1);
}
pthread_exit((void*)101);
//return (void*)200;
}
int main(){
pthread_t tid;
pthread_create(&tid, NULL, thr, NULL);
sleep(5);
pthread_cancel(tid);
void *ret;
pthread_join(tid, &ret);
printf("return value:%d\n", (int)ret);
return 0;
}
注意:pthread_cancel能够执行成功的前提是要终止的线程里必须有Cancellation points。也就是说线程里不能光是一个空的死循环,循环里至少要有一行代码,否则pthread_cancel不能执行成功。
pthread_detach函数:从父线程中分离出线程,也就是说这个子线程不受父线程的控制了,父线程不再需要回收这个子线程的资源了,也就是不可以调用pthread_join函数去回收它所占的资源了。
#include <pthread.h>
int pthread_detach(pthread_t thread);
- thread:线程ID
- 返回值:成功返回0;失败返回errno
例子:验证分离后,不可以调用pthread_join函数。
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void * thr(void* args){
while(1){
printf("in thread %d, %lu\n", getpid(), pthread_self());
sleep(1);
}
pthread_exit((void*)101);
//return (void*)200;
}
int main(){
pthread_t tid;
pthread_create(&tid, NULL, thr, NULL);
sleep(5);
pthread_detach(tid);
int ret = pthread_join(tid, NULL);
if(ret != 0){
printf("return value:%d, %s\n", ret, strerror(ret));
}
return 0;
}
结果分析:打印出下面的,errno是22.
return value:22, Invalid argument
查看线程库函数的版本:NPTL 2.27(NPTL:Native POSIX Thread Library)
getconf GNU_LIBPTHREAD_VERSION
NPTL 2.27
线程创建多少个性能好?
CPU核数*2 + 2
pthread_equal函数:判断2个线程ID是否相同
#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
虽然线程ID的类型是pthread_t,也就是无符号长整形,但是也不推荐用==去判断,因为Linux后续的版本有可能把pthread_t类型变为构造。
注意:在同一个进程内,线程ID是唯一的。但是在不同的进程里,可以参数相同的线程ID。这点和进程的ID不同,进程ID肯定是唯一的。
创建线程时,pthread_create函数的第二参数,可以设置线程的属性。
设置线程属性的步骤:
1,调用pthread_attr_init函数
#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr);
2,设置属性。设置属性的函数如下:
pthread_attr_getaffinity_np pthread_attr_setaffinity_np
pthread_attr_getdetachstate pthread_attr_setdetachstate
pthread_attr_getguardsize pthread_attr_setguardsize
pthread_attr_getinheritsched pthread_attr_setinheritsched
pthread_attr_getschedparam pthread_attr_setschedparam
pthread_attr_getschedpolicy pthread_attr_setschedpolicy
pthread_attr_getscope pthread_attr_setscope
pthread_attr_getstack pthread_attr_setstack
pthread_attr_getstackaddr pthread_attr_setstackaddr
pthread_attr_getstacksize pthread_attr_setstacksize
3,调用pthread_attr_destroy函数
#include <pthread.h>
int pthread_attr_destroy(pthread_attr_t *attr);
举例:先指定线程的属性是detatch,然后再创建线程。创建线程后就不用再调用pthread_detach函数了。这么做的好处是,如果线程的执行时间特别短,还没调用pthread_detach函数,线程就结束了的情况,程序也可以正常回收线程的资源。
#include <stdio.h>
#include <pthread.h>
#include <string.h>
void * thr(void* arg){
printf("thread\n");
}
int main(){
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_t tid;
pthread_create(&tid, &attr, thr, NULL);
int ret = pthread_join(tid, NULL);
if(ret > 0){
printf("ret:%d, %s\n", ret, strerror(ret));
}
pthread_attr_destroy(&attr);
}
线程传递参数的例子
用malloc开辟内存空间,记得释放。
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
void * thr(void* arg){
int* pi = arg;
printf("%d thread\n", *pi);
*pi = 100 + *pi;
return pi;
}
int main(){
pthread_t tid[5];
int* pi[5];
for(int j = 0; j < 5; ++j){
pi[j] = (int*)malloc(sizeof(int));
}
int i = 0;
for(; i < 5; ++i){
*pi[i] = i;
pthread_create(&tid[i], NULL, thr, pi[i]);
}
void* ret;
for(i = 0; i < 5; ++i){
pthread_join(tid[i], &ret);
printf("ret:%d\n", *(int*)ret);
}
for(int i = 0; i < 5; ++i){
free(pi[i]);
}
}
c/c++ 学习互助QQ群:877684253
本人微信:xiaoshitou5854
linux 线程基础的更多相关文章
- Linux线程基础
复习中掌握线程的基本管理即可,而不用考虑线程的同步: 创建线程花费的代价,比创建进程小得多,所以同一个进程的,多个线程执行多个任务-->比多个进程执行多个任务更有效率. 线程也分为用户级线程.内 ...
- 被linux线程基础折磨的点滴——还是没得到完美的答案,但至少得到了所需
#include<sys/types.h> #include<unistd.h> #include<stdio.h> #include<stdlib.h> ...
- Linux线程基础函数
1. 线程标识: (1) 比较两个线程ID: #include <pthread.h> int pthread_equal(pthread_t tid1, pthread_t tid2); ...
- Linux 系统应用编程——线程基础
传统多任务操作系统中一个可以独立调度的任务(或称之为顺序执行流)是一个进程.每个程序加载到内存后只可以唯一地对应创建一个顺序执行流,即传统意义的进程.每个进程的全部系统资源是私有的,如虚拟地址空间,文 ...
- [转载]Linux 线程实现机制分析
本文转自http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/ 支持原创.尊重原创,分享知识! 自从多线程编程的概念出现在 Linux ...
- Linux进程基础
Linux进程基础 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 计算机实际上可以做的事情实质上非常简单,比如计算两个数的和 ...
- Linux线程学习(二)
线程基础 进程 系统中程序执行和资源分配的基本单位 每个进程有自己的数据段.代码段和堆栈段 在进行切换时需要有比较复杂的上下文切换 线程 减少处理机的空转时间,支持多处理器以及减少上下文切换开销, ...
- Linux 线程与进程,以及通信
http://blog.chinaunix.net/uid-25324849-id-3110075.html 部分转自:http://blog.chinaunix.net/uid-20620288-i ...
- Qt之线程基础
何为线程 线程与并行处理任务息息相关,就像进程一样.那么,线程与进程有什么区别呢?当你在电子表格上进行数据计算的时候,在相同的桌面上可能有一个播放器正在播放你最喜欢的歌曲.这是一个两个进程并行工作的例 ...
随机推荐
- 渗透测试学习 十五、 文件上传&&解析漏洞
大纲:文件解析漏洞 上传本地验证绕过 上传服务器验证绕过 文件解析漏洞 解析漏洞主要说的是一些特殊文件被IIS.Apache.Nginx在某些情况下解释成脚本文件格式的漏洞. IIS 5.x/6.0解 ...
- Centos系统配置bond0
版权声明:本文为博主原创文章,支持原创,转载请附上原文出处链接和本声明. 本文链接地址:https://www.cnblogs.com/wannengachao/p/11942254.html 1.查 ...
- 01-CSS3-justify-content: space-around; justify-content: space-between;
/* justify-content: space-around; 运用在父级元素上 第一个子元素距离左边的距离==最后一个子元素距离右边的距离 除第一个子元素和最后一个子元素外,第2个,第3个... ...
- 14.Java基础_函数/函数重载/参数传递
Java函数和函数重载 /* 函数定义: public static 返回类型 func(参数){ 方法体: } 函数重载 在调用时,Java虚拟机会通过参数的不同来区分同名的函数 满足: 1.多个函 ...
- zabbix使用钉钉告警
1.钉钉创建群 2.[root@localhost ~]# vim /etc/zabbix/zabbix_server.conf # 配置文件中查找”Alert”查看告警脚本存放路径 [root@lo ...
- MySQL 王者晋级之路
3.2 Query Cache: 3.3 存储引擎 一.TokuDB的特点: – 插入性能加快20到80倍– 压缩数据减少存储空间– 数据量可扩展到几个TB– 不会产生索引碎片– 支持Hot Colu ...
- WEB 中的文件下载(待修改、完善)
在 WEB 开发中,我们会期望用户在点击某个链接的时候,下载一个文件(不管这个文件能不能被浏览器解析,都要下载).以前接触过一种方式,就是在响应 header 中设置 force-download : ...
- JPA简介
JPA的学习 JPA是Java Persistence API的简称,中文名Java持久层API.是Java EE5.0平台中Sun为了统一持久层ORM框架而制定的一套标准,注意是一套标准,而不是具体 ...
- angular和ionic4对过滤器pipe的使用
以下为自定义过滤器 import { Pipe, PipeTransform, Injectable } from '@angular/core'; import { DomSanitizer} fr ...
- 突然看到原来除了jar包还有war包啊?????
先来说说区别: 首先,jar包呢,是一个压缩文件,可以由很多文件压缩而成,,简单来说就是,jar包是别人写好的一些类,然后对这些类 进行打包,这就是jar包,你可以直接用这些 jar包,使用里面的类 ...