C线程大全

322 阅读28分钟

C线程

 线程属性管理函数:
 ​
 pthread_attr_init:初始化线程属性对象。
 pthread_attr_destroy:销毁线程属性对象。
 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:获取/设置线程的栈大小。
 pthread_attr_getaffinity_np / pthread_attr_setaffinity_np:获取/设置线程的CPU亲和性。
 线程管理函数:
 ​
 pthread_create:创建一个新线程。
 pthread_exit:退出当前线程。
 pthread_join:等待一个线程的结束并获取其返回值。
 pthread_cancel:请求取消一个线程的执行。
 pthread_tryjoin_np / pthread_timedjoin_np:非阻塞/超时等待一个线程的结束并获取其返回值。
 pthread_self:获取当前线程的线程标识符。
 互斥锁管理函数:
 ​
 pthread_mutex_init / pthread_mutex_destroy:初始化/销毁互斥锁。
 pthread_mutex_lock / pthread_mutex_trylock / pthread_mutex_unlock:加锁/尝试加锁/解锁互斥锁。
 pthread_mutex_consistent:通知系统互斥锁状态一致。
 pthread_mutexattr_getpshared / pthread_mutexattr_setpshared:获取/设置互斥锁的共享属性。
 pthread_mutexattr_getrobust / pthread_mutexattr_setrobust:获取/设置互斥锁的鲁棒性属性。
 读写锁管理函数:
 ​
 pthread_rwlockattr_getkind_np / pthread_rwlockattr_setkind_np:获取/设置读写锁的类型。
 自旋锁管理函数:
 ​
 pthread_spin_init / pthread_spin_destroy:初始化/销毁自旋锁。
 pthread_spin_lock / pthread_spin_trylock / pthread_spin_unlock:加锁/尝试加锁/解锁自旋锁。
 信号管理函数:
 ​
 pthread_sigmask:设置/获取线程的信号屏蔽集。
 pthread_sigqueue:向线程发送信号。
 其他函数:
 ​
 pthread_yield:主动放弃当前线程的执行权。
 pthread_getname_np / pthread_setname_np:获取/设置线程的名称。
 pthread_getschedparam:获取线程的调度参数。
 您可以根据需要使用这些函数来实现多线程程序中的线程管理、同步与通信等功能。
 union pthread_attr_t
 {
   char __size[__SIZEOF_PTHREAD_ATTR_T];
   long int __align;
 };
 #  define __SIZEOF_PTHREAD_ATTR_T 56
  1. __size: 这是一个字符数组,大小为 __SIZEOF_PTHREAD_ATTR_T,它可能用于存储线程属性对象的实际数据。
  2. __align: 这是一个长整型,用于确保联合体的对齐

在这里,__align 是一个用于对齐的成员。在 C 语言中,对齐是指将变量放置在内存中的地址位置,以满足硬件或者编译器对内存对齐的要求。通常,变量的地址应该是其大小的整数倍,这样访问这个变量的速度会更快,因为它们更容易被 CPU 识别和处理。

在这个联合体中,__size 是一个字符数组,其大小为 __SIZEOF_PTHREAD_ATTR_T,而 __align 是一个长整型。长整型通常要求更大的内存对齐,所以将 __align 添加到联合体中可以确保整个联合体的对齐。

1.创建线程

  #include <pthread.h>
 ​
        int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                           void *(*start_routine) (void *), void *arg);
 ​
        Compile and link with -pthread.
 ​
 DESCRIPTION
        The  pthread_create()  function  starts  a  new thread in the calling process.  The new thread starts execution by invoking
        start_routine(); arg is passed as the sole argument of start_routine().
 ​
  • thread: 指向线程标识符指针。
  • attr:一个不透明的属性对象,可以被用来设置线程属性,也可以使用默认值 NULL。
  • start_routine:线程运行函数起始地址,一旦线程被创建就会执行。
  • arg:运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL。

创建线程成功时,函数返回 0,若返回值不为 0 则说明创建线程失败。 创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行代码。

2.退出进程

    #include <pthread.h>
    void pthread_exit(void *retval);

通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。 如果 main() 是在它所创建的线程之前结束,并通过 pthread_exit() 退出,那么其他线程将继续执行。否则,它们将在 main() 结束时自动被终止。

3.线程等待

  #include <pthread.h>
 ​
        int pthread_join(pthread_t thread, void **retval);
 ​

第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。 这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。 最后要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join的线程则返回错误代码ESRCH。

4.线程取消

 int pthread_cancel()

pthread_cancel() 函数用于请求取消指定的线程。取消请求是异步的,即函数调用会立即返回,并不会等待线程实际被取消。被取消的线程将在适当的取消点(例如函数调用)处被终止。

参数 thread 是要取消的线程的标识符,它是通过调用 pthread_create() 创建线程时返回的值。

这个函数会向指定的线程发送一个取消请求,但并不保证线程会立即终止。取消请求的处理取决于线程的取消状态以及线程所在的系统环境。被取消的线程可以通过设置取消状态为启用(enable)或者延迟(defer)来决定何时以及如何响应取消请求。

注意,pthread_cancel() 函数只是发送一个取消请求,线程是否实际被取消取决于线程本身的取消状态以及是否在取消点上。

5.线程分离

 int pthread_detach(pthread_t thread);

pthread_detach() 函数用于将一个线程标记为分离状态,这意味着该线程在退出时会自动释放其资源,而不需要其他线程调用 pthread_join() 来等待它的结束。

参数 thread 是要标记为分离状态的线程的标识符,它是通过调用 pthread_create() 创建线程时返回的值。

当您将线程标记为分离状态后,线程结束时将自动释放其资源,不会产生僵尸线程。这可以减少线程管理的复杂性,特别是对于那些不需要等待线程结束的情况。

6.线程终止

 void pthread_exit(void *retval);

pthread_exit() 函数用于终止调用它的线程,并返回一个指向任意类型的指针 retval,这个指针可以被其他线程通过 pthread_join() 函数获取。

当调用 pthread_exit() 时,当前线程立即终止执行,并释放其所占用的资源。线程的退出状态被设为 retval,这个值可以在调用 pthread_join() 函数时由其他线程获取。

7.比较线程标识符

int pthread_equal(pthread_t t1, pthread_t t2);

pthread_equal() 函数用于比较两个线程标识符,判断它们是否代表同一个线程。

参数 t1t2 是两个线程标识符,它们是通过调用 pthread_create() 创建线程时返回的值。

如果 t1t2 代表同一个线程,则 pthread_equal() 函数返回非零值(通常为 1),否则返回零。

8.发送信号

 int pthread_kill(pthread_t thread, int sig);

pthread_kill() 函数用于向指定线程发送信号 sig,以请求其终止或者执行其他操作。

  • 参数 thread 是要发送信号的目标线程的标识符,它是通过调用 pthread_create() 创建线程时返回的值。
  • 参数 sig 是要发送的信号的编号,它是一个整数值,代表了特定的信号。

调用 pthread_kill() 函数会向目标线程发送指定的信号,但并不保证线程会立即对信号做出响应。目标线程可以根据信号的类型和处理方式来决定如何处理信号。

9.获取线程id

pthread_t pthread_self(void);

pthread_self() 函数用于获取调用线程自身的线程标识符。

调用 pthread_self() 函数将返回当前线程的线程标识符,这个标识符是通过调用 pthread_create() 创建线程时返回的值

10.线程信号

int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
  • 参数 how

    指定了函数的行为:

    • 如果 how 设置为 SIG_BLOCK,则将信号集 set 中的信号添加到线程的当前信号屏蔽集中。
    • 如果 how 设置为 SIG_UNBLOCK,则将信号集 set 中的信号从线程的当前信号屏蔽集中移除。
    • 如果 how 设置为 SIG_SETMASK,则用信号集 set 替换线程的当前信号屏蔽集。
  • 参数 set 是一个指向 sigset_t 类型的指针,它指向一个信号集,表示要添加到、从、或者替换掉当前线程的信号屏蔽集的信号。

  • 参数 oldset 是一个指向 sigset_t 类型的指针,用于存储之前线程的信号屏蔽集的副本。如果 oldset 不为 NULL,则函数将当前信号屏蔽集的副本存储到 oldset 中。

10.发送信号

int pthread_sigqueue(pthread_t thread, int sig,
                            const union sigval value);
union sigval
{
  int sival_int;
  void *sival_ptr;
};

pthread_sigqueue() 函数用于向指定线程发送信号以及关联的附加数据。

  • 参数 thread 是目标线程的线程标识符,它是通过调用 pthread_create() 创建线程时返回的值。
  • 参数 sig 是要发送的信号的编号,它是一个整数值,代表了特定的信号。
  • 参数 value 是一个 union sigval 类型的联合体,它用于传递与信号相关的附加数据。这个联合体允许您传递一个整数值或者一个指针作为附加数据,具体取决于信号的类型和语义。

设置线程属性

1.init AND destory

int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
  • pthread_attr_init(pthread_attr_t *attr): 此函数用于初始化线程属性对象。它接受一个指向 pthread_attr_t 类型的指针 attr 作为参数,并在函数调用后,将该对象设置为默认值。这个函数通常在创建线程之前调用,以确保线程属性对象的正确初始化。
  • pthread_attr_destroy(pthread_attr_t *attr): 此函数用于销毁线程属性对象。它接受一个指向 pthread_attr_t 类型的指针 attr 作为参数,并在函数调用后,释放该对象所占用的资源。这个函数通常在不再需要线程属性对象时调用,以防止内存泄漏。
 Detach state        = PTHREAD_CREATE_DETACHED
                   Scope               = PTHREAD_SCOPE_SYSTEM
                   Inherit scheduler   = PTHREAD_EXPLICIT_SCHED
                   Scheduling policy   = SCHED_OTHER
                   Scheduling priority = 0
                   Guard size          = 0 bytes
                   Stack address       = 0x40197000
                   Stack size          = 0x3000000 bytes

2.设置和获取线程分离属性

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
  • pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate): 这个函数用于设置线程属性对象 attr 的分离状态属性。参数 detachstate 可以是以下两个值之一:

    • PTHREAD_CREATE_JOINABLE:表示线程将以可连接状态创建,这意味着其他线程可以通过调用 pthread_join() 等待该线程结束。
    • PTHREAD_CREATE_DETACHED:表示线程将以分离状态创建,这意味着该线程在退出时将自动释放其资源,而不需要其他线程等待。
  • pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate): 这个函数用于获取线程属性对象 attr 的分离状态属性。它从属性对象中提取分离状态的信息,并将其存储在 detachstate 指向的内存位置。通过调用这个函数,您可以查询线程的分离状态设置。

3.设置和获取线程的亲和性(Affinity)

int pthread_attr_setaffinity_np(pthread_attr_t *attr,
                          size_t cpusetsize, const cpu_set_t *cpuset);
int pthread_attr_getaffinity_np(const pthread_attr_t *attr,
                          size_t cpusetsize, cpu_set_t *cpuset);
  • pthread_attr_setaffinity_np(pthread_attr_t *attr, size_t cpusetsize, const cpu_set_t *cpuset): 这个函数用于设置线程属性对象 attr 的亲和性。参数 cpusetsize 指定了 cpuset 的大小,而 cpuset 是一个 CPU 集合,它指定了线程可以运行的 CPU 列表。这个函数允许您将线程限制在指定的 CPU 上运行,以利用特定的硬件资源或提高性能。
  • pthread_attr_getaffinity_np(const pthread_attr_t *attr, size_t cpusetsize, cpu_set_t *cpuset): 这个函数用于获取线程属性对象 attr 的亲和性。它从属性对象中提取亲和性信息,并将其存储在 cpuset 中。参数 cpusetsize 指定了 cpuset 的大小。您可以使用这个函数来查询线程的亲和性设置。

4.设置和获取栈保护区大小

       int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
       int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize);
  • pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize): 这个函数用于设置线程属性对象 attr 的栈保护区大小。栈保护区是栈的末尾,用于检测栈的溢出。参数 guardsize 指定了栈保护区的大小,以字节为单位。通过设置栈保护区的大小,可以提高检测栈溢出的效率和精度。
  • pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize): 这个函数用于获取线程属性对象 attr 的栈保护区大小。它从属性对象中提取栈保护

5.设置获取线程调度参数

int pthread_attr_setschedparam(pthread_attr_t *attr,
                                      const struct sched_param *param);
int pthread_attr_getschedparam(const pthread_attr_t *attr,
                                      struct sched_param *param);
  • pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param): 这个函数用于设置线程属性对象 attr 的调度参数。参数 param 是一个指向 sched_param 结构的指针,该结构包含了调度参数的信息,比如线程的优先级。通过调用这个函数,您可以设置线程的调度参数,例如优先级等。
  • pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param): 这个函数用于获取线程属性对象 attr 的调度参数。它从属性对象中提取调度参数的信息,并将其存储在 param 指向的内存位置。通过调用这个函数,您可以查询线程的调度参数设置。

6.设置和获取线程作用域

int pthread_attr_setscope(pthread_attr_t *attr, int scope);
int pthread_attr_getscope(const pthread_attr_t *attr, int *scope);
  • pthread_attr_setscope(pthread_attr_t *attr, int scope): 这个函数用于设置线程属性对象 attr 的作用域属性。参数 scope 指定了线程的作用域,它可以是以下两个值之一:

    • PTHREAD_SCOPE_SYSTEM:表示线程的调度范围是系统级别的,即线程可以在系统的任何处理器上运行。
    • PTHREAD_SCOPE_PROCESS:表示线程的调度范围是进程级别的,即线程只能在创建它的进程的处理器上运行。

7.设置和获取线程栈地址

int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr);
int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr);
  • pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr): 这个函数用于设置线程属性对象 attr 的栈地址属性。参数 stackaddr 是一个指针,指向要分配给线程的栈的起始地址。通过调用这个函数,您可以为线程分配特定的栈地址。
  • pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr): 这个函数用于获取线程属性对象 attr 的栈地址属性。它从属性对象中提取栈地址的信息,并将其存储在 stackaddr 指向的内存位置。

8.设置和获取线程调度基础属性

int pthread_attr_setinheritsched(pthread_attr_t *attr,
                                        int inheritsched);
int pthread_attr_getinheritsched(const pthread_attr_t *attr,
                                        int *inheritsched);
  • pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched): 这个函数用于设置线程属性对象 attr 的调度继承属性。参数 inheritsched 可以是以下两个值之一:

    • PTHREAD_INHERIT_SCHED:表示线程应该继承创建它的线程的调度属性。这意味着线程将使用创建它的线程的调度策略和优先级。
    • PTHREAD_EXPLICIT_SCHED:表示线程应该使用显式设置的调度属性。这意味着线程将不会继承创建它的线程的调度属性,而是使用自己显式设置的调度策略和优先级。
  • pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inheritsched): 这个函数用于获取线程属性对象 attr 的调度继承属性。它从属性对象中提取调度继承属性的信息,并将其存储在 inheritsched 指向的内存位置。通过调用这个函数,您可以查询线程的调度继承设置。

9.设置和获取线程调度策略

int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy)
  • pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy): 这个函数用于设置线程属性对象 attr 的调度策略属性。参数 policy 指定了线程的调度策略,它可以是以下之一:

    • SCHED_FIFO:先进先出调度策略,根据优先级进行调度。
    • SCHED_RR:轮转调度策略,根据优先级进行调度,但每个线程只能运行一段时间后就会被置于队列末尾等待。
    • SCHED_OTHER:其他调度策略,通常用于非实时线程。
  • pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy): 这个函数用于获取线程属性对象 attr 的调度策略属性。它从属性对象中提取调度策略的信息,并将其存储在 policy 指向的内存位置。通过调用这个函数,您可以查询线程的调度策略设置。

10.设置和获取线程栈属性

 int pthread_attr_setstack(pthread_attr_t *attr,
                                 void *stackaddr, size_t stacksize);
 int pthread_attr_getstack(const pthread_attr_t *attr,
                                 void **stackaddr, size_t *stacksize);
  • pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize): 这个函数用于设置线程属性对象 attr 的栈属性。参数 stackaddr 是一个指针,指向要分配给线程的栈的起始地址,而 stacksize 是栈的大小,以字节为单位。通过调用这个函数,您可以为线程分配指定大小的栈空间,并指定栈的起始地址。
  • pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize): 这个函数用于获取线程属性对象 attr 的栈属性。它从属性对象中提取栈的起始地址和大小,并将它们存储在 stackaddrstacksize 指向的内存位置。通过调用这个函数,您可以查询线程的栈属性设置。

11.设置和获取栈空间大小

 int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
 int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);
  • pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize): 这个函数用于设置线程属性对象 attr 的栈大小属性。参数 stacksize 指定了线程的栈空间大小,以字节为单位。通过调用这个函数,您可以为线程分配特定大小的栈空间。
  • pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize): 这个函数用于获取线程属性对象 attr 的栈大小属性。它从属性对象中提取栈大小的信息,并将其存储在 stacksize 指向的内存位置。通过调用这个函数,您可以查询线程的栈大小设置。

其余函数

1.清理删除

void pthread_cleanup_push(void (*routine)(void *),
                                 void *arg);
void pthread_cleanup_pop(int execute);
  • pthread_cleanup_push(void (*routine)(void *), void *arg): 这个函数用于将清理函数推入线程的清理函数堆栈中。参数 routine 是一个函数指针,指向要执行的清理函数,而 arg 是传递给清理函数的参数。在调用 pthread_cleanup_push() 时,将指定的清理函数和参数推入清理函数堆栈中,以便在后续调用 pthread_cleanup_pop() 时执行。
  • pthread_cleanup_pop(int execute): 这个函数用于从线程的清理函数堆栈中弹出一个清理函数,并根据 execute 的值决定是否执行该清理函数。如果 execute 的值为非零,则执行弹出的清理函数;如果 execute 的值为零,则不执行清理函数。在调用 pthread_cleanup_pop() 时,将清理函数从堆栈中弹出,并根据 execute 的值决定是否执行它。

2.辅助清理

 #include <pthread.h>

       void pthread_cleanup_push_defer_np(void (*routine)(void *),
                                          void *arg);
       void pthread_cleanup_pop_restore_np(int execute);
  • pthread_cleanup_push_defer_np(void (*routine)(void *), void *arg): 这个函数与标准的 pthread_cleanup_push() 函数类似,用于将清理函数推入线程的清理函数堆栈中。参数 routine 是一个函数指针,指向要执行的清理函数,而 arg 是传递给清理函数的参数。不同之处在于,pthread_cleanup_push_defer_np() 函数将清理函数推入堆栈时,会延迟执行清理函数,直到调用 pthread_cleanup_pop_restore_np() 函数时才会执行清理函数。
  • pthread_cleanup_pop_restore_np(int execute): 这个函数与标准的 pthread_cleanup_pop() 函数类似,用于从线程的清理函数堆栈中弹出一个清理函数,并根据 execute 的值决定是否执行该清理函数。与标准函数的区别在于,pthread_cleanup_pop_restore_np() 函数在执行清理函数后,会将该清理函数从堆栈中弹出,并且不会将它重新推入堆栈中。这意味着在下一次调用 pthread_cleanup_push_defer_np() 函数时,清理函数不会再被延迟执行。

3.已经处理互斥锁错误

int pthread_mutex_consistent(pthread_mutex_t *mutex);

pthread_mutex_consistent() 函数用于通知 POSIX 线程库,当前线程已经处理了一个出现在拥有错误检测属性的互斥锁上的错误,这个错误可以是 PTHREAD_MUTEX_ERRORCHECK 类型的锁处于不一致状态。

  • 参数 mutex 是一个指向要处理的互斥锁的指针,它是一个 pthread_mutex_t 类型的对象。

调用 pthread_mutex_consistent() 函数会告知线程库,当前线程已经处理了该锁上的错误,并且锁的状态现在是一致的。这个函数通常与 PTHREAD_MUTEX_ERRORCHECK 类型的锁一起使用,以确保在出现错误状态后,线程能够适当地处理这种情况,并且锁能够继续正常使用。

4.获取互斥锁属性对象共享状态

 int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr,
                                        int *pshared);
 int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr,
                                        int pshared);
  • pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr, int *pshared): 这个函数用于获取互斥锁属性对象 attr 的共享状态。它从属性对象中提取共享状态的信息,并将其存储在 pshared 指向的内存位置。共享状态可以是以下之一:

    • PTHREAD_PROCESS_PRIVATE:表示该互斥锁是进程私有的,只能由同一进程内的线程共享。
    • PTHREAD_PROCESS_SHARED:表示该互斥锁是进程共享的,可以由同一进程内的多个线程,以及不同进程内的线程共享。
  • pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared): 这个函数用于设置互斥锁属性对象 attr 的共享状态。参数 pshared 指定了要设置的共享状态。

5.获取互斥锁对象的鲁棒属性

int pthread_mutexattr_getrobust(const pthread_mutexattr_t *attr,
                                       int *robustness);
int pthread_mutexattr_setrobust(const pthread_mutexattr_t *attr,
                                       int robustness);
  • pthread_mutexattr_getrobust(const pthread_mutexattr_t *attr, int *robustness): 这个函数用于获取互斥锁属性对象 attr 的鲁棒性属性。它从属性对象中提取鲁棒性的信息,并将其存储在 robustness 指向的内存位置。鲁棒性属性可以是以下之一:

    • PTHREAD_MUTEX_STALLED:表示当拥有互斥锁的线程非正常终止时,锁将保持为已锁定状态,等待一个解锁请求。
    • PTHREAD_MUTEX_ROBUST:表示当拥有互斥锁的线程非正常终止时,锁将被标记为已释放状态,以允许其他线程继续使用该锁。
  • pthread_mutexattr_setrobust(const pthread_mutexattr_t *attr, int robustness): 这个函数用于设置互斥锁属性对象 attr 的鲁棒性属性。参数 robustness 指定了要设置的鲁棒性属性。

6.读写锁属性对象类型

	int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr,
                                          int pref);
    int pthread_rwlockattr_getkind_np(const pthread_rwlockattr_t *attr,
                                          int *pref);
  • pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref): 这个函数用于设置读写锁属性对象 attr 的类型。参数 pref 指定了要设置的读写锁类型。具体取值可以是以下之一:

    • PTHREAD_RWLOCK_PREFER_READER_NP:表示优先考虑读取器。如果设置为此类型,当存在读取器和写入器竞争时,系统会优先分配读取器。这样可以最大程度地保证读取操作的并发性能。
    • PTHREAD_RWLOCK_PREFER_WRITER_NP:表示优先考虑写入器。如果设置为此类型,当存在读取器和写入器竞争时,系统会优先分配写入器。这样可以最大程度地保证写入操作的公平性。
  • pthread_rwlockattr_getkind_np(const pthread_rwlockattr_t *attr, int *pref): 这个函数用于获取读写锁属性对象 attr 的类型。它从属性对象中提取类型信息,并将其存储在 pref 指向的内存位置。

7.初始化和销毁自旋锁

 int pthread_spin_init(pthread_spinlock_t *lock int pshared);
 int pthread_spin_destroy(pthread_spinlock_t *lock);
  • pthread_spin_init(pthread_spinlock_t *lock, int pshared): 这个函数用于初始化一个自旋锁。参数 lock 是指向自旋锁对象的指针,而 pshared 表示该自旋锁的共享属性。如果 pshared 设置为非零值,表示自旋锁是可跨进程共享的,即可以在不同进程间共享使用;如果 pshared 设置为零,表示自旋锁是进程私有的,只能在同一进程内的线程间共享使用。如果初始化成功,函数返回 0,否则返回一个错误码。
  • pthread_spin_destroy(pthread_spinlock_t *lock): 这个函数用于销毁一个自旋锁,并释放相关资源。参数 lock 是指向要销毁的自旋锁对象的指针。如果销毁成功,函数返回 0,否则返回一个错误码。

8.自旋锁使用

 int pthread_spin_lock(pthread_spinlock_t *lock);
 int pthread_spin_trylock(pthread_spinlock_t *lock);
 int pthread_spin_unlock(pthread_spinlock_t *lock);
  • pthread_spin_lock(pthread_spinlock_t *lock): 这个函数用于获取(锁定)自旋锁。如果自旋锁当前处于未锁定状态,则该函数将锁定自旋锁,并立即返回;如果自旋锁已经被其他线程锁定,则当前线程会忙等待,直到自旋锁被释放为止。
  • pthread_spin_trylock(pthread_spinlock_t *lock): 这个函数尝试获取自旋锁。如果自旋锁当前处于未锁定状态,则该函数将锁定自旋锁,并返回 0;如果自旋锁已经被其他线程锁定,则该函数立即返回,而不会发生忙等待。
  • pthread_spin_unlock(pthread_spinlock_t *lock): 这个函数用于释放(解锁)自旋锁。如果当前线程拥有自旋锁的锁定权,则该函数将释放自旋锁;如果当前线程不拥有自旋锁的锁定权,则该函数的行为是未定义的。

这些函数提供了对自旋锁的基本操作,用于实现对共享资源的互斥访问。在使用自旋锁时,应注意避免死锁和竞争条件等问题。

9.检测是否取消

void pthread_testcancel(void);

pthread_testcancel() 函数是一个 POSIX 线程库函数,用于检查当前线程是否有取消请求,并在有取消请求时立即执行取消动作。

当一个线程设置了取消状态(通过调用 pthread_setcancelstate()pthread_setcanceltype() 函数),并且另一个线程请求取消该线程时,当前线程的取消状态会被设置为 PTHREAD_CANCEL_ENABLE。这时,如果当前线程调用了 pthread_testcancel() 函数,它会立即检查是否有取消请求,并在有取消请求时执行取消动作。

通常情况下,pthread_testcancel() 函数会被放置在循环的适当位置,以便在循环执行期间能够检查取消请求,并在有取消请求时及时退出循环,从而实现对取消请求的快速响应。

10.线程退出


       int pthread_tryjoin_np(pthread_t thread, void **retval);

       int pthread_timedjoin_np(pthread_t thread, void **retval,
                                const struct timespec *abstime);
  • pthread_tryjoin_np(pthread_t thread, void **retval): 这个函数尝试等待标识为 thread 的线程退出,并获取其返回值。如果线程已经退出,则立即返回线程的退出状态,并将其返回值存储在 retval 指向的内存位置中。如果线程尚未退出,则函数会立即返回,并且 retval 不会被修改。函数返回值为 0 表示成功获取线程的返回值,非零值表示失败。
  • pthread_timedjoin_np(pthread_t thread, void **retval, const struct timespec *abstime): 这个函数在指定的超时时间内等待标识为 thread 的线程退出,并获取其返回值。参数 abstime 是一个绝对时间点,表示函数的超时时间。如果在超时时间内线程退出,则函数立即返回线程的退出状态,并将其返回值存储在 retval 指向的内存位置中;如果超时时间到达而线程尚未退出,则函数返回 ETIMEDOUT 错误,并且 retval 不会被修改。

11.放弃执行线程

int pthread_yield(void);

pthread_yield() 函数是一个 POSIX 线程库函数,用于主动放弃当前线程的执行权,使得调度器可以将执行机会转移到其他可运行线程上。

调用 pthread_yield() 函数会导致当前线程让出 CPU,然后系统调度器可以将执行权交给其他就绪状态的线程。这个函数的调用可能会导致当前线程暂时失去执行机会,直到它再次被调度到为止。

pthread_yield() 函数通常用于在多线程程序中显式地控制线程的调度,以提高线程间的公平性或者降低线程间的优先级差异。然而,使用这个函数可能会导致性能下降,因为频繁的线程切换会增加系统开销。因此,应该谨慎使用这个函数,只在必要时才使用。

xian'c

//
// Created by wangyufan on 2024-05-18.
//

#ifndef TESTLIBEVENT_HZ_PTHREAD_POOL_H
#define TESTLIBEVENT_HZ_PTHREAD_POOL_H

#include <stdio.h>
#include <pthread.h>

typedef struct hz_pthread_pool_work {
    void *(*work_routine)(void *);

    void *args;
    struct hz_pthread_pool_work *next;
} hz_pthread_pool_work;

typedef struct hz_thread_pool {
    size_t shutdown;
    size_t max_num_thread;
    pthread_t *thread_id;
    hz_pthread_pool_work *hz_thread_pool_head;
    pthread_cond_t queue_ready;
    pthread_mutex_t queue_lock;
} hz_thread_pool;

int hz_create_thread_pool(hz_thread_pool **pool, size_t max_thread_num);

void hz_destroy_thread(hz_thread_pool *pool);

int hz_add_task_to_thread_pool(hz_thread_pool *pool, void *(*routine)(void*), void *args);

#endif //TESTLIBEVENT_HZ_PTHREAD_POOL_H
//
// Created by wangyufan on 2024-05-18.
//

#include "hz_pthread_pool.h"
#include <stdlib.h>
#include <errno.h>
#include <string.h>

static void *work_routine(void *args) {
    hz_thread_pool *pool = (hz_thread_pool *) args;
    hz_pthread_pool_work *work = NULL;
    while (1) {
        pthread_mutex_lock(&pool->queue_lock);
        while (!pool->hz_thread_pool_head &&
               !pool->shutdown) { // if there is no works and pool is not shutdown, it should be suspended for being awake
            pthread_cond_wait(&pool->queue_ready, &pool->queue_lock);
        }
        if (pool->shutdown) {
            pthread_mutex_unlock(&pool->queue_lock);
            pthread_exit(NULL);
        }
        work = pool->hz_thread_pool_head;
        pool->hz_thread_pool_head = (hz_pthread_pool_work *) pool->hz_thread_pool_head->next;
        pthread_mutex_unlock(&pool->queue_lock);
        work->work_routine(work->args);
        free(work);
    }
    return NULL;

}

int hz_create_thread_pool(hz_thread_pool **pool, size_t max_thread_num) {
    (*pool) = (hz_thread_pool *) malloc(sizeof(hz_thread_pool));
    if (NULL == *pool) {
        printf("in %s,malloc hz_thread_pool failed!,errno = %d,explain:%s\n", __func__, errno, strerror(errno));
        exit(-1);
    }
    (*pool)->shutdown = 0;
    (*pool)->max_num_thread = max_thread_num;
    (*pool)->hz_thread_pool_head = NULL;
    (*pool)->thread_id = (pthread_t *) malloc(sizeof(pthread_t) * max_thread_num);
    if ((*pool)->thread_id == NULL) {
        printf("in %s,init thread id failed,errno = %d,explain:%s", __func__, errno, strerror(errno));
        exit(-1);
    }

    if (pthread_mutex_init(&((*pool)->queue_lock), NULL) != 0) {
        printf("in %s,initial mutex failed,errno = %d,explain:%s", __func__, errno, strerror(errno));
        exit(-1);
    }

    if (pthread_cond_init(&((*pool)->queue_ready), NULL) != 0) {
        printf("in %s,initial condition variable failed,errno = %d,explain:%s", __func__, errno, strerror(errno));
        exit(-1);
    }
    for (int i = 0; i < max_thread_num; i++) {
        if (pthread_create(&((*pool)->thread_id[i]), NULL, work_routine, (void *) (*pool)) != 0) {
            printf("pthread_create failed!\n");
            exit(-1);
        }

    }
    return 0;
}

int hz_add_task_to_thread_pool(hz_thread_pool *pool, void *(*routine)(void *), void *args) {
    hz_pthread_pool_work *work, *member;
    if (!routine) {
        printf("routine in NULL\n");
        return -1;
    }
    work = (hz_pthread_pool_work *) malloc(sizeof(hz_pthread_pool_work));
    if (!work) {
        printf("in %s,malloc work error!,errno = %d,explain:%s\n", __func__, errno, strerror(errno));
        return -1;
    }
    work->work_routine = routine;
    work->args = args;
    work->next = NULL;
    pthread_mutex_lock(&pool->queue_lock);
    member = pool->hz_thread_pool_head;
    if (!member) {
        pool->hz_thread_pool_head = work;
    } else {
        while (member->next) {
            member = (hz_pthread_pool_work *) member->next;
        }
        member->next = work;
    }
    pthread_cond_signal(&pool->queue_ready);
    pthread_mutex_unlock(&pool->queue_lock);
    return 0;
}

void hz_destroy_thread(hz_thread_pool *pool) {
    hz_pthread_pool_work *tmp_work;
    if (pool->shutdown)return;
    pool->shutdown = 1;
    pthread_mutex_lock(&pool->queue_lock);
    pthread_cond_broadcast(&pool->queue_ready);
    pthread_mutex_unlock(&pool->queue_lock);
    for (int i = 0; i < pool->max_num_thread; i++)
        pthread_join(pool->thread_id[i], NULL);
    free(pool->thread_id);
    while (pool->hz_thread_pool_head) {
        tmp_work = pool->hz_thread_pool_head;
        pool->hz_thread_pool_head = (hz_pthread_pool_work *) pool->hz_thread_pool_head->next;
        free(tmp_work);
    }
    pthread_mutex_destroy(&pool->queue_lock);
    pthread_cond_destroy(&pool->queue_ready);
    free(pool);
}