linux学习笔记_7.多线程

多线程

线程是轻量级的进程(LWP:light weight process),在 Linux 环境下线程的本质仍是进程。在计算机上运行的程序是一组指令及指令参数的组合,指令按照既定的逻辑控制计算机运行。操作系统会以进程为单位,分配系统资源

1、和进程相比,线程是一种非常节俭的多任务操作方式,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表维护它的代码段、堆栈段、数据段,这是一种昂贵的多任务工作方式
运行于一个进程中的多线程,它们之间使用相同的地址空间,而且线程间彼此切换所需的时间也远远小于进程间切换所需要的时间

2、线程间方便的通讯机制,对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过进程间通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便

3.除了以上说的有点外,多线程程序作为一种多任务、并发的工作方式,有如下优点:
使用CPU系统更加高效,操作系统会爆炸当线程数不大于CPU数码时,不同的线程运行与不同的CPU上
改善程序结构,一个既长又复杂的进程可以考虑分为多线程,称为几个独立或半独立的运行部分,这样的程序会利于理解和修改

Linux系统下的多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连接(编译)时需要加libpthread.a

进程是资源分配的最小单位,线程是操作系统调度执行的最小单位。

Linux进程创建一个新线程时,线程将拥有自己的栈线程,有独立的PCB,但没有独立的地址空间(共享)(由于线程有自己的局部变量),但与它的创建者共享全局变量、文件描写叙述符、信号句柄和当前文件夹状态。

Linux通过fork创建子进程与创建线程之间是有差别的:fork创建出该进程的一份拷贝,这个新进程拥有自己的变量和自己的PID,它的时间调度是独立的,它的运行差点儿全然独立于父进程。

多线程实现

应用功能线程进程
创建pthread_createfork,vfork
退出pthread_exitexit
等待pthread_joinwait、waitpid
取消/终止pthread_cancelabort
读取IDpthread_selfgetpid
调度策略SCHED_OTHER,SCHED_FIFO,SCHED_RRSCHED_OTHER,SCHED_FIFO,SCHED_RR
通信机制信号量、信号、互斥锁、条件变量、读写锁有名/无名通道、信号、IPC(信号量、消息队列、共享内存)
线程创建
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);

参数

  • thread:线程的id,传出参数

  • arrr: 代表线程的属性。一般为NULL

  • 第三个参数 函数指针 , void* func(void*)

  • arg: 线程执行函数的参数

返回值:

成功返回0,失败返回errno

线程退出、等待
void pthread_exit( void * value_ptr );

线程的终止可以是调用了pthread_exit或者该线程的例程结束。也就是说,一个线程可以隐式的退出,也可以显式的调用pthread_exit函数来退出。
pthread_exit函数唯一的参数value_ptr是函数的返回代码

int pthread_join( pthread_t  thread, void * * value_ptr );

函数pthread_join的作用是,等待一个线程终止。
调用pthread_join的线程将被挂起直到参数thread所代表的线程终止时为止。pthread_join是一个线程阻塞函数,调用它的函数将一直等到被等待的线程结束为止。

参数

  • thread:被连接线程的线程号
  • **retval: 指向一个指向被连接线程的返回码的指针的指针

返回值:

成功返回0,失败返回非0

线程取消
int pthread_cancel(pthread_t thread);

发送终止信号给thread线程,如果成功则返回0,否则为非0值。发送成功并不意味着thread会终止。

pthread_cancel调用并不等待线程终止,它只提出请求。线程在取消请求(pthread_cancel)发出后会继续运行,
直到到达某个取消点(CancellationPoint)。取消点是线程检查是否被取消并按照请求进行动作的一个位置.

int pthread_setcancelstate(int state, int *oldstate)
设置本线程对Cancel信号的反应,state有两种值:PTHREAD_CANCEL_ENABLE(缺省)和PTHREAD_CANCEL_DISABLE,
分别表示收到信号后设为CANCLED状态和忽略CANCEL信号继续运行;old_state如果不为NULL则存入原来的Cancel状态以便恢复。

int pthread_setcanceltype(int type, int *oldtype)
设置本线程取消动作的执行时机,type由两种取值:PTHREAD_CANCEL_DEFFERED和PTHREAD_CANCEL_ASYCHRONOUS,仅当Cancel状态为Enable时有效,分别表示收到信号后继续运行至下一个取消点再退出和立即执行取消动作(退出);oldtype如果不为NULL则存入运来的取消动作类型值。

void pthread_testcancel(void)
是说pthread_testcancel在不包含取消点,但是又需要取消点的地方创建一个取消点,以便在一个没有包含取消点的执行代码线程中响应取消请求.
线程取消功能处于启用状态且取消状态设置为延迟状态时,pthread_testcancel()函数有效。
如果在取消功能处处于禁用状态下调用pthread_testcancel(),则该函数不起作用。
请务必仅在线程取消线程操作安全的序列中插入pthread_testcancel()。除通过pthread_testcancel()调用以编程方式建立的取消点意外,pthread标准还指定了几个取消点。测试退出点,就是测试cancel信号.

线程取消的方法是向目标线程发Cancel信号,但如何处理Cancel信号则由目标线程自己决定,或者忽略、或者立即终止、或者继续运行至Cancelation-point(取消点),由不同的Cancelation状态决定。

线程接收到CANCEL信号的缺省处理(即pthread_create()创建线程的缺省状态)是继续运行至取消点,也就是说设置一个CANCELED状态,线程继续运行,只有运行至Cancelation-point的时候才会退出。

线程私有数据
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));

第一个参数为指向一个键值的指针,第二个参数指明了一个destructor函数,

如果这个参数不为空,那么当每个线程结束时,系统将调用这个函数来释放绑定在这个键上的内存块。

key一旦被创建,所有线程都可以访问它,但各线程可根据自己的需要往key中填入不同的值,这就相当于提供了一个同名而不同值的全局变量,一键多值。

一键多值靠的是一个关键数据结构数组即TSD池,创建一个TSD就相当于将结构数组中的某一项设置为“in_use”,并将其索引返回给*key,然后设置清理函数。

int pthread_key_delete(pthread_key_t key);	

key:需要删除的键.成功返回0.其他任何返回值都表示出了错误。

销毁线程特定数据键。由于键已无效,因此将释放与该键关联的所有内存。在调用该函数之前必须释放所有线程的特定资源,该函数不会调用任何析构函数。反复调用pthread_key_create与pthread_key_delete可能会产生问题。对于每个所需的键,应当只调用pthread_key_create一次。

int pthread_setspecific(pthread_key_t key, const void *value);

使用pthread_setspecific可以为指定线程特定数据键设置线程特定绑定
形参:key:需要关联的键value:指向需要关联的数据成功返回0.其他任何返回值都表示出了错误。如果出现以下任一情况,pthread_setspecifi将失败并返回相应的值: ENOMEM:虚拟内存不足 ;EINVAL:key无效

注意:value必须是动态内存分配,否则在其他函数使用getsetpecific时会出现错误。同时记得在destrucotr中释放value指向的内存,防止内存泄露

void *pthread_getspecific(pthread_key_t key);

使用pthread_getspecific获取调用线程的键绑定,并将该绑定存储在value指向的位置中.

key:需要获取数据的键,不返回任何错误

线程同步

同步就是协同步调,按预定的先后次序进行运行。线程同步是指多线程通过特定的设置(如互斥量,事件对象,临界区)来控制线程之间的执行顺序也可以说是在线程之间通过同步建立起执行顺序的关系,如果没有同步,那线程之间是各自运行各自的!

线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步。

互斥锁

互斥锁本质就是一个特殊的全局变量,拥有lock和unlock两种状态,unlock的互斥锁可以由某个线程获得,当互斥锁由某个线程持有后,这个互斥锁会锁上变成lock状态,此后只有该线程有权力打开该锁,其他想要获得该互斥锁的线程都会阻塞,直到互斥锁被解锁。

互斥锁的类型:

  • 普通锁(PTHREAD_MUTEX_NORMAL):互斥锁默认类型。当一个线程对一个普通锁加锁以后,其余请求该锁的线程将形成一个 等待队列,并在该锁解锁后按照优先级获得它,这种锁类型保证了资源分配的公平性。一个 线程如果对一个已经加锁的普通锁再次加锁,将引发死锁;对一个已经被其他线程加锁的普 通锁解锁,或者对一个已经解锁的普通锁再次解锁,将导致不可预期的后果。

  • 检错锁(PTHREAD_MUTEX_ERRORCHECK):一个线程如果对一个已经加锁的检错锁再次加锁,则加锁操作返回EDEADLK;对一个已 经被其他线程加锁的检错锁解锁或者对一个已经解锁的检错锁再次解锁,则解锁操作返回 EPERM。

  • 嵌套锁(PTHREAD_MUTEX_RECURSIVE):该锁允许一个线程在释放锁之前多次对它加锁而不发生死锁;其他线程要获得这个锁,则当前锁的拥有者必须执行多次解锁操作;对一个已经被其他线程加锁的嵌套锁解锁,或者对一个已经解锁的嵌套锁再次解锁,则解锁操作返回EPERM。

  • 默认锁(PTHREAD_MUTEX_ DEFAULT):一个线程如果对一个已经加锁的默认锁再次加锁,或者虽一个已经被其他线程加锁的默 认锁解锁,或者对一个解锁的默认锁解锁,将导致不可预期的后果;这种锁实现的时候可能 被映射成上述三种锁之一。

// 静态方式创建互斥锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 

// 动态方式创建互斥锁,其中参数mutexattr用于指定互斥锁的类型,具体类型见上面四种,如果为NULL,就是普通锁。
int pthread_mutex_init (pthread_mutex_t* mutex,const pthread_mutexattr_t* mutexattr);

int pthread_mutex_lock(pthread_mutex_t *mutex); // 加锁,阻塞
int pthread_mutex_trylock(pthread_mutex_t *mutex); // 尝试加锁,非阻塞
int pthread_mutex_unlock(pthread_mutex_t *mutex); // 解锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);//销毁互斥锁
读写锁

读写锁可以有三种状态:读模式下加锁状态,写模式下加锁状态,不加锁状态。一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁。读写锁也叫做共享-独占锁,当读写锁以读模式锁住时,它是以共享模式锁住的,当它以写模式锁住时,它是以独占模式锁住的,读读共享,读写互斥。

// 创建读写锁
pthread_rwlock_t rwlock=PTHREAD_RWLOCK_INITIALIZER;

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); //初始化
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); //销毁

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); // 加读锁,阻塞
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); // 加写锁,阻塞
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); // 释放读锁或者写锁

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); // 尝试加读锁,非阻塞
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); // 尝试加写锁,非阻塞
条件变量

条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待”条件变量的条件成立”而挂起;另一个线程使”条件成立”(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起
条件变量和互斥锁一样,都有静态动态两种创建方式,静态方式使用PTHREAD_COND_INITIALIZER常量,

pthread_cond_t cond=PTHREAD_COND_INITIALIZER;

int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
int pthread_cond_destroy(pthread_cond_t *cond);

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); //无条件等待
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, 
                           const struct timespec *abstime);			//计时等待

int pthread_cond_broadcast(pthread_cond_t *cond);//则激活所有等待线程
int pthread_cond_signal(pthread_cond_t *cond);	//激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个

信号量

Linux下主要分为两种信号量,system-v和posix信号量,posix信号量又分为无名信号量和有名信号量。信号量是同步的一种方式,常常用于对共享资源的访问。

信号量特点

  1. 信号量会使等待资源线程进入休眠状态,所以适合于那些占用资源比较久的场合,比较车倒车状态的切换。
  2. 在驱动中,信号量不可以用于中断中,原因是信号量会引起休眠状态,中断不能休眠。
  3. 若共享资源的持有时间比较短,则不适合使用信号量,频繁的休眠,切换线程会导致开销远远大于信号量所带来的那点优势。
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared,unsigned int value);
/*
sem :信号量变量;
pshared:是否在多个进程中传递信号量,若不是则写为0;
value:该参数指定信号量的初始值。如果想要在两个进程之间使用信号量,需要确保sem参数指向两个进程之间共享的内存范围。*/
int sem_destroy(sem_t *sem); //销毁
//P操作
int sem_trywait(sem_t *sem);
int sem_wait(sem_t *sem);
//returns 0 on success; on error,-1 is returned, 
//如果信号量的值大于0,则此时会将信号量的值减1,并且立刻返回,若当前信号量的值为0,那么sem_wait会导致线程阻塞,直到信号量的值大于0或者被信号中断才返回,而sem_trywait为非阻塞版本,当信号量为0时,该函数回-1并且将errno置为EAGAIN。
//V操作
int sem_post(sem_t *sem);
//该函数用于给信号量计数+1,若此时用sem_wait阻塞的线程则被唤醒。

int sem_t getvalue(sem_t sem, int *val); //取值操作

线程异步

线程的异步机制只有信号,类似于线程的信号。

在linux下,每个进程都有自己的signal mask,这个信号掩码指定哪个信号被阻塞,哪个不会被阻塞,通常用调用sigmask来处理。同时每个进程还有自己的signal action,这个行为集合指定了信号该如何处理,通常调用sigaction来处理。

信号

线程信号具备以下特点

  • 任何线程都可以向其它线程(同一进程下)发送信号;
  • 每个线程都具备自己独立的信号屏蔽集,不影响其它线程;
  • 线程创建时,不继承原线程的信号屏蔽集;
  • 同进程下,所有线程共享对某信号的处理方式,即一个设置,所有有效;
  • 多个线程的程序,向某一个线程发送终止信号,则整个进程终止
int pthread_kill(thread_t tid, int sig); //向线程发送信号函数

功能:
向线程发送信号。

参数:
tid:线程ID。
sig:信号编号。

返回值:
成功:0。
失败: -1。

int pthread_sigmask(int how, const sigset_t *act, sigset_t *oldact);//更改或检查调用线程的信号掩码函数

功能:
更改或检查调用线程的信号掩码。

参数
how:向当前的信号掩码中添加或删除或替换set。
act:信号屏蔽字。
oldact:为NULL,即可。

how说明:
SIG_BLOCK: 结果集是当前集合参数集的并集(把参数set中的信号添加到信号屏蔽字中)
SIG_UNBLOCK: 结果集是当前集合参数集的差集(把信号屏蔽字设置为参数set中的信号)
SIG_SETMASK: 结果集是由参数集指向的集(从信号屏蔽字中删除参数set中的信号)

返回值:
成功:0。
失败: -1。

线程属性控制

线程结构体:

  typedef struct{
      int                        detachstate;     //线程的分离状态
      int                        schedpolicy;    //线程调度策略
      struct sched_param         schedparam; //线程的调度参数
      int                        inheritsched; //线程的继承性
      int                        scope;              //线程的作用域
      size_t                     guardsize;       //线程栈末尾的警戒缓冲区大小
      int                        stackaddr_set; //线程的栈设置
      void*                      stackaddr;       //线程栈的位置(最低地址)
      size_t                     stacksize;        //线程栈的大小
      } pthread_attr_t; 
1) __detachstate

默认属性的线程在执行完目标函数后,占用的私有资源并不会立即释放,要么执行完 pthread_join() 函数后释放,要么整个进程执行结束后释放。某些场景中,我们并不需要接收线程执行结束后的返回值,如果想让线程执行完后立即释放占用的私有资源,就可以通过修改 __detachstate 属性值来实现。

__detachstate 属性值用于指定线程终止执行的时机,该属性的值有两个,分别是:

  • PTHREAD_CREATE_JOINABLE(默认值):线程执行完函数后不会自行释放资源;
  • PTHREAD_CREATE_DETACHED:线程执行完函数后,会自行终止并释放占用的资源。
    关于 __detachstate 属性,<pthread.h> 头文件中提供了 2 个与它相关的函数,分别是:
int pthread_attr_getdetachstate(const pthread_attr_t * attr,int * detachstate);
int pthread_attr_setdetachstate(pthread_attr_t *sttr,int detachstate);

pthread_attr_getdetachstate() 函数用于获取 __detachstate 属性的值,detachstate 指针用于接收 __detachstate 属性的值;pthread_attr_setdetachstate() 函数用于修改 __detachstate 属性的值,detachstate 整形变量即为新的 __detachstate 属性值。两个函数执行成功时返回数字 0,反之返回非零数。

此外,<pthread.h> 头文件还提供有 pthread_detach() 函数,可以直接将目标线程的 __detachstate 属性改为 PTHREAD_CREATE_DETACHED,语法格式如下:

int pthread_detach(pthread_t thread);

函数执行成功时返回数字 0 ,反之返回非零数。

2) __schedpolicy

__schedpolicy 属性用于指定系统调度该线程所用的算法,它的值有以下 3 个:

  • SCHED_OTHER(默认值):分时调度算法;
  • SCHED_FIFO:先到先得(实时调度)算法;
  • SCHED_RR:轮转法;

其中,SCHED_OTHER 调度算法不支持为线程设置优先级,而另外两种调度算法支持。

<pthread.h> 头文件提供了如下两个函数,专门用于访问和修改 __schedpolicy 属性:

int pthread_attr_getschedpolicy(const pthread_attr_t *, int * policy)
int pthread_attr_setschedpolicy(pthread_attr_*, int policy)

pthread_attr_getschedpolicy() 函数用于获取当前 __schedpolicy 属性的值;pthread_attr_setschedpolicy() 函数用于修改 __schedpolicy 属性的值。函数执行成功时,返回值为数字 0,反之返回非零数。

3) __schedparam

__scheparam 用于设置线程的优先级(默认值为 0),该属性仅当线程的 __schedpolicy 属性为 SCHED_FIFO 或者 SCHED_RR 时才能发挥作用。

<pthread.h> 头文件中提供了如下两个函数,用于获取和修改 __schedparam 属性的值:

int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);

其中,param 参数用于接收或者修改 __scheparam 属性的优先级,它是 sched_param 结构体类型的变量,定义在 <sched.h> 头文件中,内部仅有一个 sched_priority 整形变量,用于表示线程的优先级。函数执行成功时返回数字 0,反之返回非零数。

当需要修改线程的优先级时,我们只需创建一个 sched_param 类型的变量并为其内部的 sched_priority 成员赋值,然后将其传递给 pthrerd_attr_setschedparam() 函数。

不同的操作系统,线程优先级的值的范围不同,您可以通过调用如下两个系统函数获得当前系统支持的最大和最小优先级的值:

int sched_get_priority_max(int policy);   //获得最大优先级的值
int sched_get_priority_min(int policy);   //获得最小优先级的值

其中,policy 的值可以为 SCHED_FIFO、SCHED_RR 或者 SCHED_OTHER,当 policy 的值为 SCHED_OTHER 时,最大和最小优先级的值都为 0。

4) __inheritsched

新建线程的调度属性(____schedpolicy 和 __schedparam 属性)默认遵循父线程的属性(谁创建它,谁就是它的父线程),如果我们想自定义线程的调度属性,就需要借助 __inheritsched 属性。

也就是说,新线程的调度属性要么遵循父线程,要么遵循 myAttr 规定的属性,默认情况下 __inheritsched 规定新线程的调度属性遵循父线程,我们也可以修改 __inheritsched 的值,使新线程的调度属性遵循自定义的属性变量(如文章开头定义的 myAttr)规定的值。

<pthread.h> 头文件提供了如下两个函数,分别用于获取和修改 __inheritsched 属性的值:

//获取 __inheritsched 属性的值
int pthread_attr_getinheritsched(const pthread_attr_t *attr,int *inheritsched);
//修改 __inheritsched 属性的值
int pthread_attr_setinheritsched(pthread_attr_t *attr,int inheritsched);

其中在 pthread_attr_setinheritsched() 函数中,inheritsched 参数的可选值有两个,分别是:

  • PTHREAD_INHERIT_SCHED(默认值):新线程的调度属性继承自父线程;
  • PTHREAD_EXPLICIT_SCHED:新线程的调度属性继承自 myAttr 规定的值。

以上两个函数执行成功时返回数字 0,反之返回非零数。

5) __scope

线程执行过程中,可以只和同进程内的其它线程争夺 CPU 资源,也可以和系统中所有的其它线程争夺 CPU 资源,__scope 属性用于指定目标线程和哪些线程抢夺 CPU 资源。

<pthread.h> 头文件中提供了如下两个函数,分别用于获取和修改 __scope 属性的值:

//获取 __scope 属性的值
int pthread_attr_getscope(const pthread_attr_t * attr,int * scope);
//修改 __scope 属性的值
int pthread_attr_setscope(pthread_attr_t * attr,int * scope);

当调用 pthread_attr_setscope() 函数时,scope 参数的可选值有两个,分别是:

  • PTHREAD_SCOPE_PROCESS:同一进程内争夺 CPU 资源;
  • PTHREAD_SCOPE_SYSTEM:系统所有线程之间争夺 CPU 资源。

Linux系统仅支持 PTHREAD_SCOPE_SYSTEM,即所有线程之间争夺 CPU 资源。

当函数执行成功时,返回值为数字 0,反之返回非零数。

6) __stacksize

每个线程都有属于自己的内存空间,通常称为栈(有时也称堆栈、栈空间、栈内存等)。某些场景中,线程执行可能需要较大的栈内存,此时就需要我们自定义线程拥有的栈的大小。

__stacksize 属性用于指定线程所拥有的栈内存的大小。<pthread.h> 提供有以下两个函数,分别用于获取和修改栈空间的大小:

//获取当前栈内存的大小
int pthread_attr_getstacksize(const pthread_attr_t * attr,size_t * stacksize);
//修改栈内存的大小
int pthread_attr_setsstacksize(pthread_attr_t * attr,size_t * stacksize);

函数执行成功时,返回值为数字 0,反之返回非零数。

7) __guardsize

每个线程中,栈内存的后面都紧挨着一块空闲的内存空间,我们通常称这块内存为警戒缓冲区,它的功能是:一旦我们使用的栈空间超出了额定值,警戒缓冲区可以确保线程不会因“栈溢出”立刻执行崩溃。

__guardsize 属性专门用来设置警戒缓冲区的大小,<pthread.h> 头文件中提供了如下两个函数,分别用于获取和修改 __guardsize 属性的值:

int pthread_attr_getguardsize(const pthread_attr_t *restrict attr,size_t *restrict guardsize);
int pthread_attr_setguardsize(pthread_attr_t *attr ,size_t *guardsize);

pthread_attr_setguardsize() 函数中,设置警戒缓冲区的大小为参数 guardsize 指定的字节数。函数执行成功时返回数字 0,反之返回非零数。

//初始化线程属性(给结构体分配空间,相当于malloc)
int pthread_attr_init(pthread_attr_t *attr); 成功:0;失败:错误号
//销毁线程属性所占用的资源(释放结构体资源,相当于free)
int pthread_attr_destroy(pthread_attr_t *attr); 成功:0;失败:错误号
//获取线程ID。其作用对应进程中 getpid() 函数。    
pthread_t pthread_self(void); 返回值:成功:0; 失败:无
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值