Linux系统编程教程之Linux线程函数的使用:讲解Linux线程函数

简介: Linux系统编程教程之Linux线程函数的使用:讲解Linux线程函数


本文介绍Linux下线程的常用接口


线程基本操作相关函数

  • pthread_create(创建线程)
int pthread_create(
    pthread_t *thread,                 //线程ID
    const pthread_attr_t *attr,        //线程属性
    void *(*start_routine) (void *),   //回调函数
    void *arg                        //回调函数的参数
);

参数:

thread为指向线程标识符(线程id)的指针。

attr用来设置线程属性。设NULL表示默认属性

start_routine是线程运行函数的起始地址。

arg是运行函数的参数。

返回值:

    若成功,返回0

    若失败,返回出错编号并且*thread中的内容是未定义的

  • pthread_exit(终止当前线程)
void pthread_exit(void *status);
//终止当前线程,所有绑定在线程数据键上的内存将被释放。

说明:

如果当前线程是非分离的,那么这个线程的标示符合退出代码将被保留,直到其他线程用pthread_join来等待当前线程的终止。

如果当前线程是分离的,status将被忽略,线程标示符将被立即回收。

参数:

status:不为NULL时,线程的退出代码被置为status参数指向的值。

  • pthread_cancel(退出线程)
int pthread_cancel(pthread_t thread);
//退出一个线程。如何响应退出请求取决于目标线程的状态。

返回值:

    若成功,返回0

    若失败,返回出错编号

  • pthread_detach(分离线程)
int pthread_detach(pthread_t tid);
//将非分离的线程设置为分离线程。

说明:

即通知线程库在指定的线程终止时回收线程占用的内存等资源。

在一个线程上使用多次pthread_detach的结果是不可预见的。

返回值:

    若成功,返回0

    若失败,返回出错编号

  • pthread_join(等待线程结束)
int pthread_join(pthread_t tid, void **status);
等待一个线程结束,该函数阻塞调用它线程,直到参数tid指定的线程结束。

说明:

不能有多个线程等待同一个线程终止。如果出现这种情况,一个线程将成功返回,别的线程将返回错误ESRCH

参数:

tid:指定的线程必须在当前进程中,同时tid指定的线程必须是非分离的。

status:不为NULL时,接收线程tid的退出状态(返回值)

返回值:

    若成功,返回0

    若失败,返回出错编号


线程属性相关函数

线程属性结构如下:

typedef struct
{
       int                               detachstate;   //线程的分离状态
       int                               schedpolicy;   //线程调度策略
       structsched_param                 schedparam;    //线程的调度参数
       int                               inheritsched;  //线程的继承性
       int                               scope;         //线程的作用域
       size_t                            guardsize;     //线程栈末尾的警戒缓冲区大小
       int                               stackaddr_set; //线程堆栈设置
       void*                             stackaddr;     //线程堆栈位置
       size_t                            stacksize;     //线程栈的大小
}pthread_attr_t;
  • 初始化/反初始化属性
int pthread_attr_init(pthread_attr_t *attr);
//把属性设置为默认值
 
int pthread_attr_destroy(pthread_attr_t *attr);
//如果初始化函数分配了与属性对象关联的资源,摧毁函数负责释放这些资源

返回值:

    若成功,返回0

    若失败,返回出错编号

  • 获取/修改线程的分离状态属性
int pthread_attr_getdetachstate(const pthread_attr_t* attr,int* detachstate );
//从线程属性对象attr获取线程分离状态属性,并在detachstate中返回它。
 
int pthread_attr_setdetachstate(pthread_attr_t* attr,int detachstate );
//将线程属性对象attr中的线程分离状态属性设置为detachstate。

参数:

attr:指向pthread_attr_t结构的指针,用于定义创建新线程时要使用的属性。

detachstate:指向一个位置的指针,该函数可以存储线程分离状态:

PTHREAD_CREATE_JOINABLE     - 以可连接状态创建线程(默认值)。

PTHREAD_CREATE_DETACHED   - 在分离状态下创建线程。

返回值:

    若成功,返回0

    若失败,返回出错编号

  • 获取/设置线程创建栈属性
int pthread_attr_getstack(const pthread_attr_t *restrict attr,void **restrict stackaddr, size_t *restrict stacksize);
//获取线程创建栈属性stackaddr和stacksize。
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr,size_t stacksize);
//设置线程创建栈属性stackaddr和stacksize。

参数:

stackaddr :堆栈属性指定要用于创建的线程堆栈的存储区域的基地址(最低可寻址字节)

(stackaddr和0x7)不为0,pthread_attr_setstack()可能会失败[EINVAL]。

stacksize:存储器的大小,应至少为{PTHREAD_STACK_MIN}。堆叠堆栈应适当对齐,用作堆叠;

stackaddr和stacksize描述的堆栈内的所有页面都应该是线程可读写的。

返回值:

    若成功,返回 0 , pthread_attr_getstack()函数将堆栈属性值存储在stackaddr和stacksize中。

    若失败,返回出错编号

  • 获取/设置线程栈大小属性
int pthread_attr_getstacksize(const pthread_attr_t* attr,size_t* stacksize );
//从线程属性对象attr获取线程栈大小属性,并在stacksize中返回它。
 
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);

说明:

堆栈溢出保护可以浪费系统资源。创建许多线程的应用程序可以通过关闭保护区域来保护系统资源,如果它信任其线程不会溢出堆栈。

当线程在堆栈上分配大的对象时,需要大的保护来检测堆栈溢出。

参数:

attr :指向pthread_attr_t结构的指针,用于定义创建新线程时要使用的属性。

stacksize:指向一个位置的指针,该函数可以存储要用于新线程的堆栈大小。

返回值:

    若成功,返回0

    若失败,返回出错编号

  • 获取/设置线程的保护区大小
int pthread_attr_getguardsize(const pthread_attr_t* attr,size_t* guardsize );
//从属性结构attr获取线程守护属性的值。
 
int pthread_attr_setguardsize(pthread_attr_t* attr, size_t guardsize );
//将属性结构attr中线程的保护区域的大小设置为guardsize。

说明:

该防区有助于防止堆叠溢出;在堆栈的溢出端分配额外内存的保护字节。

如果一个线程溢出到这个缓冲区,它会接收一个SIGSEGV信号。

参数:

attr :指向pthread_attr_t结构的指针,用于定义创建新线程时要使用的属性。

guardsize:控制着线程栈末尾之后用以避免栈溢出的扩展内存大小。

返回值:

    若成功,返回0

    若失败,返回出错编号

  • 获取/设置线程的继承性
int pthread_attr_getinheritsched(const pthread_attr_t*attr,int *inheritsched);
 
int pthread_attr_setinheritsched(pthread_attr_t *attr,intinheritsched);
 
//继承性决定调度的参数是从创建的进程中继承还是使用在schedpolicy和schedparam属性中显式设置的调度信息。

说明:

Pthreads不为inheritsched指定默认值,因此如果你关心线程的调度策略和参数,必须先设置该属性。

参数:

attr:指向属性对象的指针,

inheritsched:继承性或指向继承性的指针。

继承性的可能值是

PTHREAD_INHERIT_SCHED(表示新现成将继承创建线程的调度策略和参数)

PTHREAD_EXPLICIT_SCHED(表示使用在schedpolicy和schedparam属性中显式设置的调度策略和参数)。

如果你需要显式的设置一个线程的调度策略或参数,那么你必须在设置之前将inheritsched属性设置PTHREAD_EXPLICIT_SCHED

返回值:

    若成功,返回0

    若失败,返回出错编号

  • 取/设置线程的调度策略
int pthread_attr_getschedpolicy(const pthread_attr_t*attr,int *policy);
 
int pthread_attr_setschedpolicy(pthread_attr_t *attr,intpolicy);

参数:

attr:指向属性对象的指针

intpolicy:调度策略或指向调度策略的指针。

调度策略可能的值是

  1. 先进先出(SCHED_FIFO)
  2. 轮转法(SCHED_RR)
  3. 其它(SCHED_OTHER)

SCHED_FIFO策略允许一个线程运行直到有更高优先级的线程准备好,或者直到它自愿阻塞自己。

在SCHED_FIFO调度策略下,当有一个线程准备好时,除非有平等或更高优先级的线程已经在运行,否则它会很快开始执行。

SCHED_RR(轮循)策略是基本相同的,不同之处在于:

如果有一个SCHED_RR策略的线程执行了超过一个固定的时期(时间片间隔)没有阻塞,而另外的SCHED_RR或SCHBD_FIPO策略的相同优先级的线程准备好时,运行的线程将被抢占以便准备好的线程可以执行。

返回值:

    若成功,返回0

    若失败,返回出错编号

说明:

当有SCHED_FIFO或SCHED_RR策赂的线程在一个条件变量上等持或等持加锁同一个互斥量时,它们将以优先级顺序被唤醒。

即,如果一个低优先级的SCHED_FIFO线程和一个高优先级的SCHED_FIFO线程都在等待锁相同的互斥,

则当互斥量被解锁时,高优先级线程将总是被首先解除阻塞。

  • 调度参数得到线程可设置的最大最低优先级
int sched_get_priority_max(int policy);
int sched_get_priority_min(int policy);

SCHED_OTHER 是不支持优先级使用的,它最大和最小优先级都是0。
SCHED_FIFO SCHED_RR 支持优先级的使用,他们分别为1和99,数值越大优先级越高 。

  • 获取/设置线程的调度参数
int pthread_attr_getschedparam(const pthread_attr_t*attr,struct sched_param *param);
//修改线程的优先级。
int pthread_attr_setschedparam(pthread_attr_t *attr,conststruct sched_param *param);
//获取线程的优先级。

参数:

attr:指向属性对象的指针

param:sched_param结构或指向该结构的指针。    

结构sched_param在文件/usr/include/bits/sched.h中定义如下:

struct sched_param
{
       intsched_priority;
};

结构sched_param的子成员sched_priority控制一个优先权值,大的优先权值对应高的优先权。系统支持的最大和最小优先权值可以用sched_get_priority_max函数和sched_get_priority_min函数分别得到。

返回值:

    若成功,返回0

    若失败,返回出错编号

说明:

如果不是编写实时程序,不建议修改线程的优先级。

因为,调度策略是一件非常复杂的事情,如果不正确使用会导致程序错误,从而导致死锁等问题。

如:在多线程应用程序中为线程设置不同的优先级别,有可能因为共享资源而导致优先级倒置。

  • 线程的作用域
 
#include <pthread.h>   
int    pthread_attr_getscope( const pthread_attr_t * attr, int * scope );
int    pthread_attr_setscope( pthread_attr_t*, int scope );

/*************************************
作用域控制线程是否在进程内或在系统级上竞争资源,可能的值是
PTHREAD_SCOPE_PROCESS(进程内竞争资源)
PTHREAD_SCOPE_SYSTEM   (系统级竞争资源)。
/**************************************


线程特定数据相关函数

  • 创建线程键
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
//在进程中分配一个键值,这个键被用来表示一个线程数据项。

说明:

这个键对进程中所有的线程都是可见的。刚创建线程数据键时,在所有线程中和这个键相关联的值都是NULL。

函数成功返回后,分配的键放在key参数指向的内存中,必须保证key参数指向的内存区的有效性。

如果指定了解析函数destructor,那么当线程结束时并且将非空的值绑定在这个键上,系统将调用destructor函数,参数就是相关线程与这个键绑定的值。绑定在这个键上的内存块可由destructor函数释放。

参数:

key: pthread_key_t 变量

destructor:一个清理函数(析构函数),用来在线程释放该线程存储的时候被调用。该函数指针可以设成 NULL ,这样系统将调用默认的清理函数。

返回值:

    若成功,返回0

    若失败,返回出错编号

  • 销毁线程键
int pthread_key_delete(pthread_key_t key);
//销毁线程特定数据键。由于键已无效,因此将释放与该键关联的所有内存。

说明:

在调用该函数之前必须释放所有线程的特定资源,该函数不会调用任何析构函数。

反复调用pthread_key_create与pthread_key_delete可能会产生问题。

对于每个所需的键,应当只调用pthread_key_create一次。

参数:

key:需要删除的键

返回值:

    若成功,返回0

    若失败,返回出错编号

  • 线程特定的数据管理
void *pthread_getspecific(pthread_key_t key);
//获得线程特定数据的地址, 将返回当前绑定到指定键的值代表调用线程。
int pthread_setspecific(pthread_key_t key, const void *value);
// 把键和线程特定数据相关联

说明:

将线程特定值与通过先前调用pthread_key_create()获取的密钥相关联。

不同的线程可以将不同的值绑定到相同的密钥。

这些值通常指向已被保留供调用线程使用的动态分配的存储器的块的指针。

未使用pthread_key_delete()删除pthread_key_create()或key之后的键值的pthread_getspecific()或pthread_setspecific()的函数已被删除的效果。

pthread_getspecific()和pthread_setspecific()可以从线程特定的数据析构函数调用。

对被破坏的线程特定数据密钥的pthread_getspecific()的调用将返回值NULL,除非通过调用pthread_setspecific()来更改该值(在析构函数启动之后)。

从线程特定的数据析构函数调用pthread_setspecific()可能会导致丢失的存储(至少PTHREAD_DESTRUCTOR_ITERATIONS尝试破坏)或无限循环。

这两个功能都可以实现为宏。

返回值:

pthread_getspecific:

  pthread_getspecific()函数将返回与给定键相关联的线程特定数据值。

  如果没有线程特定的数据值与密钥相关联,则返回值NULL

pthread_setspecific:    

   若成功,pthread_setspecific()函数将返回0;

   若失败,返回出错编号

  • 动态包初始化
int pthread_once(pthread_once_t *once_control,void (*init_routine)(void));
//使用初值为PTHREAD_ONCE_INIT的once_control变量保证init_routine()函数在本进程执行序列中仅执行一次

说明:

从pthread_once()返回时,init_routine应该已经完成了。

 如果已经有线程通过pthread_once调用过这个初始化函数一次,那么以后通过pthread_once函数再调用这个初始化函数将无效。

参数:

once_control: 决定是否调用了相关联的初始化例程。

init_routine:必须是一个非本地变量,而且必须初始化为PTHREAD_ONCE_INIT。

返回值:

    若成功,返回0

    若失败,返回出错编号


线程调度相关函数

  • 出让执行权
int sched_yield(void);
//导致调用线程放弃CPU。该线程被移动到队列的末尾以获得其静态优先级和a新线程运行。

参数:  

     sig:若为0,则不发送任何信号,但仍然执行错误检查; 这可以用来检查线程ID的存在。

返回值:

    若成功,返回0

    若失败,返回-1,并设置errno

  • 修改/获取优先级
int pthread_setschedparam(pthread_t tid, int policy, const struct sched_param *param);
//修改线程的优先权。
int pthread_getschedparam(pthread_t tid, int policy, struct schedparam *param);
//获取线程的优先级。

返回值:

    若成功,返回0

    若失败,返回-1,并设置errno


线程信息相关函数

  • 比较线程
int pthread_equal(pthread_t tid1, pthread_t tid2);

说明:

如果tid1和tid2相同,函数返回一个非0值,否则返回0。

如果tid1或tid2中任何一个是非法值,则返回将是不可预料的。

返回值:

    若两个线程ID相等,返回非0值

    若不相等,则返回0

  • 获取线程标示符
pthread_t pthread_self(void);
//返回当前线程的标示符。
  • 设置线程掩码
int pthread_sigmask(int how, const sigset_t *new, sigset_t *old);
//改变或检验当前线程的信号掩码。

参数:

how:表示对当前信号掩码进行什么操作,有如下值:SIG_BLOCK、SIG_UNBLOCK、SIG_SETMASK

new:当参数new为NULL时,不论how的值是什么,当前线程的信号掩码都不会改变。

old:旧的信号掩码保存在参数old指向的内存中,当old不为NULL时。

返回值:

    若成功,返回0

    若失败,返回出错编号

  • 设置可取消状态(允许/禁止退出线程)
int pthread_setcancelstate(int state,int * oldstate);
//原则上将调用线程的可取消状态设置为指示状态,并在oldstate引用的位置返回先前的可取消状态。 

参数:

state:状态的合法值为

PTHREAD_CANCEL_ENABLE(启动时的默认可取消状态):线程将在下一个取消点上对所有挂起的取消请求进行处理

PTHREAD_CANCEL_DISABLE :对pthread_cancel的调用并不会杀死线程,相反,取消请求对这个线程来说还处于挂起状态。

返回值:

    若成功,返回0

    若失败,返回出错编号

  • 设置可取消类型(退出类型)
int pthread_setcanceltype(int type,int * oldtype);
 
//原则上将调用线程的可取消类型设置为指定的类型,并在oldtype引用的位置返回先前的可取消类型。 

参数:

type:类型的合法值为

PTHREAD_CANCEL_DEFERRED(默认值)PTHREAD_CANCEL_ASYNCHRONOUS

当一个线程被创建后,缺省值是延迟类型。在异步方式下,线程可以在执行的任何时候被退出。

返回值:

    若成功,返回0

    若失败,返回出错编号

  • 创建一个取消点
void pthread_testcancel(void);
//将在调用线程中创建一个取消点。 

说明:

只有当线程的退出状态是允许退出的,而且线程的退出类型是延迟时,调用该函数才有效。

如果调用时线程的退出状态是禁止的,则该调用不起作用。小心使用该函数,只有在能够安全的被退出的地方才能够设置退出点

  • 压入/弹出善后处理函数
void pthread_cleanup_push(void (*routine)(void *), void *args);
//将一个善后处理函数压入善后处理函数堆栈。
 
void pthread_cleanup_pop(int execute);
//从善后处理函数堆栈中弹出一个善后处理函数。如果参数execute非0,则执行弹出的函数;如果参数为0,则不执行弹出函数。
 

如果一个线程显式或隐式的调用pthread_exit()函数或线程接受了退出请求,线程库实际上将会以非0参数调用pthread_cleanup_pop函数。

  • 发送信号
int pthread_kill(pthread_t tid, int sig);
//向tid指定的线程发送一个信号

参数:

tid:指定的线程必须和当前线程在同一个进程中。

sig:参数为0时,函数将进行错误检查,不发送信号,这常常用来检查tid的合法性。

返回值:

    若成功,返回0

    若失败,返回出错编号


std::thread 的使用

目录
相关文章
|
7天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
4天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
7天前
|
Linux Python
Linux 中某个目录中的文件数如何查看?这篇教程分分钟教会你!
在 Linux 系统中,了解目录下的文件数量是常见的需求。本文介绍了多种方法,包括使用 `ls` 和 `wc` 命令组合、`find` 命令、`tree` 命令以及编程方式(如 Python)。无论你是新手还是有经验的用户,都能找到适合自己的方法。掌握这些技巧将提高你在 Linux 系统中的操作效率。
18 4
|
7天前
|
Java UED
Java中的多线程编程基础与实践
【10月更文挑战第35天】在Java的世界中,多线程是提升应用性能和响应性的利器。本文将深入浅出地介绍如何在Java中创建和管理线程,以及如何利用同步机制确保数据一致性。我们将从简单的“Hello, World!”线程示例出发,逐步探索线程池的高效使用,并讨论常见的多线程问题。无论你是Java新手还是希望深化理解,这篇文章都将为你打开多线程的大门。
|
15天前
|
安全 程序员 API
|
8天前
|
安全 Java 编译器
Java多线程编程的陷阱与最佳实践####
【10月更文挑战第29天】 本文深入探讨了Java多线程编程中的常见陷阱,如竞态条件、死锁、内存一致性错误等,并通过实例分析揭示了这些陷阱的成因。同时,文章也分享了一系列最佳实践,包括使用volatile关键字、原子类、线程安全集合以及并发框架(如java.util.concurrent包下的工具类),帮助开发者有效避免多线程编程中的问题,提升应用的稳定性和性能。 ####
34 1
|
11天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
12天前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
40 4
|
12天前
|
消息中间件 供应链 Java
掌握Java多线程编程的艺术
【10月更文挑战第29天】 在当今软件开发领域,多线程编程已成为提升应用性能和响应速度的关键手段之一。本文旨在深入探讨Java多线程编程的核心技术、常见问题以及最佳实践,通过实际案例分析,帮助读者理解并掌握如何在Java应用中高效地使用多线程。不同于常规的技术总结,本文将结合作者多年的实践经验,以故事化的方式讲述多线程编程的魅力与挑战,旨在为读者提供一种全新的学习视角。
38 3
|
13天前
|
安全 Java 调度
Java中的多线程编程入门
【10月更文挑战第29天】在Java的世界中,多线程就像是一场精心编排的交响乐。每个线程都是乐团中的一个乐手,他们各自演奏着自己的部分,却又和谐地共同完成整场演出。本文将带你走进Java多线程的世界,让你从零基础到能够编写基本的多线程程序。
29 1