线程基础函数

查看进程中有多少个线程,查看线程的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 线程基础的更多相关文章

  1. Linux线程基础

    复习中掌握线程的基本管理即可,而不用考虑线程的同步: 创建线程花费的代价,比创建进程小得多,所以同一个进程的,多个线程执行多个任务-->比多个进程执行多个任务更有效率. 线程也分为用户级线程.内 ...

  2. 被linux线程基础折磨的点滴——还是没得到完美的答案,但至少得到了所需

    #include<sys/types.h> #include<unistd.h> #include<stdio.h> #include<stdlib.h> ...

  3. Linux线程基础函数

    1. 线程标识: (1) 比较两个线程ID: #include <pthread.h> int pthread_equal(pthread_t tid1, pthread_t tid2); ...

  4. Linux 系统应用编程——线程基础

    传统多任务操作系统中一个可以独立调度的任务(或称之为顺序执行流)是一个进程.每个程序加载到内存后只可以唯一地对应创建一个顺序执行流,即传统意义的进程.每个进程的全部系统资源是私有的,如虚拟地址空间,文 ...

  5. [转载]Linux 线程实现机制分析

    本文转自http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/ 支持原创.尊重原创,分享知识! 自从多线程编程的概念出现在 Linux ...

  6. Linux进程基础

    Linux进程基础   作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 计算机实际上可以做的事情实质上非常简单,比如计算两个数的和 ...

  7. Linux线程学习(二)

    线程基础 进程 系统中程序执行和资源分配的基本单位 每个进程有自己的数据段.代码段和堆栈段 在进行切换时需要有比较复杂的上下文切换   线程 减少处理机的空转时间,支持多处理器以及减少上下文切换开销, ...

  8. Linux 线程与进程,以及通信

    http://blog.chinaunix.net/uid-25324849-id-3110075.html 部分转自:http://blog.chinaunix.net/uid-20620288-i ...

  9. Qt之线程基础

    何为线程 线程与并行处理任务息息相关,就像进程一样.那么,线程与进程有什么区别呢?当你在电子表格上进行数据计算的时候,在相同的桌面上可能有一个播放器正在播放你最喜欢的歌曲.这是一个两个进程并行工作的例 ...

随机推荐

  1. Java学习笔记(1)--- 简介

    简介: 注:和之前的C不一样,本人没有学过java,所以内容上可能会相对详细一些. 主要特性: 1.这跟c++一样是面对对象语言: Java语言提供类.接口和继承等面向对象的特性,为了简单起见,只支持 ...

  2. unity_animator_stop_replay(重新播放)

    对于一个Animation重复播放,但在隐藏后,再次显示播放会出现有些属性未复原 问题描述 特效同事给的Animation中更改了物体的很多属性,如Active,Alpha, Scale,Positi ...

  3. 初学JavaScript正则表达式(十)

    前瞻与后顾 断言 === assert 符合断言为正向,不符合为负向 例 'a2*3'.replace(/\w(?=\d)/g,'x') ------- x2*3 看看'\d'前面是不是'\w',如果 ...

  4. web的前台、后台、前端、后端

    前台:呈现给用户的视觉和基本的操作.后台:用户浏览网页时,我们看不见的后台数据跑动.后台包括前端,后端.前端:对应我们写的html .javascript 等网页语言作用在前端网页.后端:对应jsp. ...

  5. opencv 截取任意四边形区域的图像

    截取任意四边形区域的图像. mask就是结果. 需要定义四边形区域,分别是 tl, tr, bl, br std::map<int, std::set<int>> genera ...

  6. matlib调用python时转py格式为matlib格式

    因为需要,我用matlib调用python代码. 调用成功但是遇到问题 如下 调用完的结果为python格式   (py.list,py.xx) matlib根本不能用 查了半天一个能解决的方法都没 ...

  7. pointNet代码

    介绍 组成 1.PointNet classification network分类网络 part segmentation network 数据集 1.point clouds sampled fro ...

  8. Redis Pipelining

    Redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务.这意味着通常情况下一个请求会遵循以下步骤: 客户端向服务端发送一个查询请求,并监听Socket返回,通常是以阻塞模式,等待服务端响 ...

  9. 【BZOJ5457】城市(线段树合并)

    点此看题面 大致题意: 一棵树上每个点有颜色\(a_i\)和权值\(b_i\),求以每个点为根的子树内权值和最大的颜色及其权值和. 线段树合并 这是一道线段树合并板子题. (关于线段树合并,可参考我的 ...

  10. 网络流 之 dinic 算法

    网络流指的是:网络流(network-flows)是一种类比水流的解决问题方法.(类似于水管群,有一个源点(水无限多),和一个汇点,最大流就代表这个点水管群(边集)每秒最大能送道汇点的水量) 这个怎么 ...