转载出处:https://blog.csdn.net/skyroben/article/details/72793409

一、背景知识

Linux没有真正意义上的线程,它的实现是由进程来模拟,所以属于用户级线程,位于libpthread共享库(所以线程的ID只在库中有效),遵循POSIX标准。

Windows下有一个真正的数据结构TCB来描述线程。

Linux上两个最有名的线程库LinuxThreads和NPTL。

Linux两个线程模型的比较:

Linux线程模型的比较

Linux下多线程虚拟地址空间的映射类似于用vfork创建多个子进程。

二、进程和线程的区别

进程:程序的一个动态运行实例,承担分配系统资源的实例。(Linux实现进程的主要目的是资源独占)

线程:在进程的内部运行(进程的地址空间)运行的一个分支,也是调度的基本单位(调度按LWP调度)。(Linux实现线程的主要目的是资源共享)

线程所有的资源由进程提供。

单进程:只有一个进程的线程(LWP=PID)。

LWP:轻量级进程。

由于同一进程的多个线程共享同一地址空间,因 此Text Segment、Data Segment都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:

1. 文件描述符表
2. 每种信号的处理方式(SIG_IGN、SIG_DFL或者自定义的信号处理函数)  
3. 当前工作目录
4. 用户id和组id
 但有些资源是每个线程各有一份的:
 1.线程ID
2. 上下文信息,包括各种寄存器的值、程序计数器和栈指针
3. 栈空间
4. errno变量
5. 信号屏蔽字
6. 调度优先级

多线程程序的优点(相对进程比较而言):

1. 多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间,创建销毁速度快。
2.是线程间方便的通信机制。由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。
 

三、进程控制

在Linux系统下,与线程相关的函数都定义在pthread.h头文件中。
 

创建线程函数——pthread_create函数

#include <pthread.h>
int pthread_create(pthread_t * thread, const pthread_arrt_t* attr,void*(*start_routine)(void *), void* arg)
(1)thread参数是新线程的标识符,为一个整型。
(2)attr参数用于设置新线程的属性。给传递NULL表示设置为默认线程属性。
(3)start_routine和arg参数分别指定新线程将运行的函数和参数。start_routine返回时,这个线程就退出了
(4)返回值:成功返回0,失败返回错误号。
 
    线程id的类型是thread_t,它只在当前进程中保证是唯一的,在不同的系统中thread_t这个类型有不同的实现,调用pthread_self()可以获得当前线程的id
    进程id的类型时pid_t,每个进程的id在整个系统中是唯一的,调用getpid()可以获得当前进程的id,是一个正整数值。
 

终止线程——pthread_cancel函数和pthread_exit函数

终止某个线程而不终止整个进程,可以有三种方法:
1. 从线程函数return。这种方法对主线程不适应,从main函数return相当于调用exit。
2. 一个线程可以调用pthread_cancel终止同一进程中的另一个线程。
3. 线程可以调用pthread_exit终止自己。
 
#include <pthread.h>

int pthread_cancel(pthread_t thread);
(1)thread参数是目标线程的标识符。
(2)该函数成功返回0,失败返回错误码。
#include <pthread.h>

void pthread_exit(void * retval);
(1)retval是void *类型,其它线程可以调用pthread_join获得这个指针。需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是由malloc分 配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

(2)pthread_exit函数通过retval参数向线程的回收者传递其退出信息。它执行之后不会返回到调用者,且永远不会失败。

 

线程等待——pthread_join

#include <pthread.h>

void pthread_join(pthread_t thread,void ** retval);

(1)调用该函数的线程将挂起等待,直到id为thread的线程终止。

(2)thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,
 
总结如下:
1. 如果thread线程通过return返回,value_ptr所指向的单元里存放的是thread线程函数的返回值。
2. 如果thread线程被别的线程调用pthread_cancel异常终掉,value_ptr所指向的单元里存放的是常数PTHREAD_CANCELED。
3. 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ptr参数。
(3)成功返回0,失败返回错误码。可能出现的错误码:
 

四、分离线程

 1.在任何一个时间点上,线程是可结合的(joinable)或者是分离的(detached)。
 2.一个可结合的线程能够被其他线程收回其资源和杀死。在被其他线程回收之前,它的存储器资源
(例如栈)是不释放的。(默认情况下线程的创建都是可结合的) 
 3.一个分离的线程是不能被其他线程回收或杀死的,它的存储器 资源在它终止时由系统自动释放。

4. 如果一个可结合线程结束运行但没有被join,会导致部分资源没有被回收,所以创建线程者应该调用pthread_join来等待线程运行结束,并可得到线程的退出代码,回收其资源。

      
调用pthread_join后,如果该线程没有运行结束,调用者会被阻塞。如何解决这种情况呢?      
    例如,在Web服务器中当主线程为每个新来的连接请求创建一个子线程进行处理的时候,主线程并不希望因为调用pthread_join而阻塞(因为还要继续处理之后到来的连接请求),这时可以在子线程中加入代码 pthread_detach(pthread_self())或者父线程调用pthread_detach(thread_id)(非阻塞,可立即返回)这将该子线程的状态设置为分离的(detached),如此一来,该线程运行结束后会自动释放所有资源。
 
验证代码:
#include <stdio.h>
#include <error.h>
#include <stdlib.h>
#include <pthread.h> void* thread_run(void* _val)
{
pthread_detach(pthread_self()); //注释这句代码join success
printf("%s\n", (char*)_val);
return NULL;
} int main(){
pthread_t tid;
int tret = pthread_create(&tid, NULL, thread_run, "thread_run~~~~~");
//线程创建成功之后,程序的执行流变成两个,一个执行函数thread_run,一个继续向下执行。
if (tret == ){
sleep();
int ret = pthread_join(tid, NULL);
if (ret == ){
printf("pthread_join success\n");
return ret;
}else{
printf("pthread_join failed info: %s\n", strerror(ret));
return ret;
}
}else{
printf("create pthread failed info: %s", strerror(tret));
return tret;
}
}
运行结果:
分析:
可以看到被分离的线程不可以被等待;一个线程初始为可结合的,要么在父线程等待或分离,要么在子线程分离,只能采取上述几种措施的一种。

[转] Linux多线程编程之pthread的更多相关文章

  1. [转]c++多线程编程之pthread线程深入理解

    多线程编程之pthread线程深入理解         Pthread是 POSIX threads 的简称,是POSIX的线程标准.           前几篇博客已经能给你初步的多线程概念.在进一 ...

  2. 多线程编程之pthread线程深入理解

    不同的平台和操作系统上 进程和线程的实现机制不完全一致  但是一般来说线程栈都是独立的 只要得到地址就可以相互访问       Pthread是 POSIX threads 的简称,是POSIX的线程 ...

  3. Linux应用编程之lseek详解

    Linux应用编程之lseek详解 1.lseek函数介绍 (1).文件指针:当我们要对一个文件进行读写时,一定要先打开这个文件,所以我们读写的所有文件都是动态文件.动态文件在内存中的形态就是文件流的 ...

  4. iOS多线程编程之NSThread的使用

      目录(?)[-] 简介 iOS有三种多线程编程的技术分别是 三种方式的有缺点介绍 NSThread的使用 NSThread 有两种直接创建方式 参数的意义 PS不显式创建线程的方法 下载图片的例子 ...

  5. iOS多线程编程之NSThread的使用(转)

    本文由http://blog.csdn.net/totogo2010/原创 1.简介: 1.1 iOS有三种多线程编程的技术,分别是: 1..NSThread 2.Cocoa NSOperation  ...

  6. [转] iOS多线程编程之Grand Central Dispatch(GCD)介绍和使用

    介绍: Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,以优化的应用程序支持多核心处理器和其他的对称多处理系统的系统.这建立在任务并行执行的线程池模式的基础上的.它首 ...

  7. [转] iOS多线程编程之NSOperation和NSOperationQueue的使用

    <iOS多线程编程之NSThread的使用> 介绍三种多线程编程和NSThread的使用,这篇介绍NSOperation的使用. 使用 NSOperation的方式有两种, 一种是用定义好 ...

  8. [转]iOS多线程编程之NSThread的使用

    1.简介: 1.1 iOS有三种多线程编程的技术,分别是: 1..NSThread 2.Cocoa NSOperation (iOS多线程编程之NSOperation和NSOperationQueue ...

  9. iOS 多线程编程之Grand Central Dispatch(GCD)

    介绍: Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,以优化的应用程序支持多核心处理器和其它的对称多处理系统的系统.这建立在任务并行运行的线程池模式的基础上的. 它 ...

随机推荐

  1. layer最大话.最小化.还原回调方法

    layer.open({              type: 1,             title: ‘在线调试‘,           content: ‘这里是内容‘,            ...

  2. docker mysql 容器报too many connections 引发的liunx磁盘扩容操作

    症状每次删除mysql容器重启没两分钟又报标题错 df -h 命令查看各个挂载空间应用情况发现root home var 三个文件目录挂载的空间满了 网上百度了一下liunx磁盘扩容操作,fdisk ...

  3. linux是什么与如何学习(三)

     1.1Linux是什么 Linux是在计算机上面运作的,所以说是一组软件. 1.2 Linux是什么?操作系统还是应用程序? 计算机主机是由一套硬件所组成的,为了有效的控制这些硬件资源,于是就有了操 ...

  4. excel常用快捷键和技巧

    1. Ctrl+方向键,对单元格光标快速移动,移动到数据边缘(空格位置). 2. Ctrl+方向键,对单元格光标快速移动,移动到数据边缘(空格位置). 3. Ctrl+A,选择整张表. 4. Ctrl ...

  5. 第十周java总结

    Java IO 1.file类 file类的构造方法: public Flie(String pathname) -->实例化Flie类的时候,必须设置好路径. 如:Flie f = new F ...

  6. [转帖]linux文件描述符文件/etc/security/limits.conf

    linux文件描述符文件/etc/security/limits.conf https://blog.csdn.net/fanren224/article/details/79971359 需要多学习 ...

  7. Java Web - 笔记(1)

    1.web.xml Attribute "xmlns:xsi" must be declared for element type "web-app"相关报错解 ...

  8. <<C++ Primer>> 第四章 表达式

    术语表 第 4 章 表达式 算术转换(arithmetic conversion): 从一种算术类型转换成另一种算术类型.在二元运算符的上下文中,为了保留精度,算术转换通常把较小的类型转换成较大的类型 ...

  9. 2017.9.23 C组比赛总结

    今天又回到了C组,感觉爽歪歪~分数终于是个三位数了,yes! 第一题,赛车.水!只用一个贪心就可以AC了. first,以速度为关键字小到大qsort一下... scond,枚举每一个赛车,看看它可以 ...

  10. 组合&多态&封装

    目录 组合&多态&封装 一.组合 1.1什么是组合 1.2 为什么要用组合 1.3 如何使用组合 1.4 继承和组合都在什么时候用 二.多态与多态性 2.1 什么是多态 2.2 如何用 ...