LinuxC线程

简介: LinuxC线程

1. 线程ID

1. 1 pthread_self()

获得当前线程的线程ID

#include <pthread.h>
pthread_t pthread_self(void);

1.2 pthread_equal()

比较两个线程ID是否相等

#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);

2. 创建线程

2.1 pthread_create()

#include <pthread.h> 
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

thread: pthread_t类型指针,创建成功时会将线程ID存在该变量内

attr: 指向 pthread_attr_t 类型的缓冲区,pthread_attr_t 数据类型定义了线程的各种属性(线程栈地址,栈大小等等),如果传递NULL表示使用默认属性创建线程

start_routine: 线程函数

arg: 线程函数的参数,是一个指针

返回值: 成功返回0;失败会返回错误码。

  • 编译时需要加上: -lpthread

3. 终止线程

线程的终止方式有三种:

  • return
  • 线程调用pthread_exit
  • 其他线程调用pthread_cancel

3.1pthread_exit()

调用 pthread_exit()相当于在线程的 start 函数中执行 return 语句,不同之处在于,可在线程 start 函数所调用的任意函数中调用 pthread_exit()来终止线程。

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

4. 回收线程

pthread_join()函数将会以阻塞的形式等待指定的线程终止,如果该线程已经终止,则 pthread_join()立刻返回。

pthread_join类似于进程的waitpid,但也有不同:

  • 线程之间的关系是对等的,进程中的任意线程均可调用 pthread_join()函数来等待另一个线程的终止
  • 不能以非阻塞的方式调用pthread_join
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);

5. 取消线程

5.1 pthread_cancel()

通过调用 pthread_cancel()库函数向一个指定的线程发送取消请求.

发出取消请求之后,函数 pthread_cancel()立即返回,不会等待目标线程的退出

#include <pthread.h>
int pthread_cancel(pthread_t thread);

5.2 pthread_setcancelstate()

设置本线程是否响应其他线程发来的取消线程请求

#include <pthread.h>
int pthread_setcancelstate(int state, int *oldstate);

state:

  • PTHREAD_CANCEL_ENABLE:线程可以取消,这是新创建的线程取消性状态的默认值,所以新建线程以及主线程默认都是可以取消的。
  • PTHREAD_CANCEL_DISABLE:线程不可被取消,如果此类线程接收到取消请求,则会将请求挂起,直至线程的取消性状态变为 PTHREAD_CANCEL_ENABLE。

5.3 pthread_setcanceltype()

如果线程的取消性状态为 PTHREAD_CANCEL_ENABLE,那么对取消请求的处理则取决于线程的取消性类型,该类型可以通过调用 pthread_setcanceltype()函数来设置,它的参数 type 指定了需要设置的类型,而线程之前的取消性类型则会保存在参数 oldtype 所指向的缓冲区中,如果对之前的类型不敢兴趣,Linux下允许将参数 oldtype 设置为 NULL.

#include <pthread.h>
int pthread_setcanceltype(int type, int *oldstate);
  • PTHREAD_CANCEL_DEFERRED:取消请求到来时,线程还是继续运行,取消请求被挂起,直到线程到达某个取消点为止,这是所有新建线程包括主线程默认的取消性类型。
  • PTHREAD_CANCEL_ASYNCHRONOUS:可能会在任何时间点(也许是立即取消,但不一定)取消线程,这种取消性类型应用场景很少,不再介绍!

5.4 取消点

取消点其实就是一些函数(通过man 7 pthreads查看有哪些,搜索Cancellation关键字),因为线程的某些操作可能是不能退出的,一旦退出会造成程序的运行发送一些不可预料的事情,故出现了取消点这个概念。在线程接收到取消请求时(并且设置了可接受取消请求),没运行到取消点之前是不会退出的。

如果线程没有上述的这些取消点,但我们又想要他取消,就可以使用该函数,目的就是产生一个取消点

#include <pthread.h>
void pthread_testcancel(void);

6. 分离线程

线程分离后能够自动回收线程资源,不需要我们写代码调用pthread_join去回收。该过程是不可逆的

一个线程即可将另一个线程分离,同时也可以将自己分离出去。

#include <pthread.h>
int pthread_detach(pthread_t thread);

7. 注册线程清理处理函数

与进程终止处理函数类似,线程在退出时也可去执行处理函数。

一个线程可以注册多个清理函数,这些清理函数记录在栈中,每个线程都可以拥有一个清理函数栈,栈是一种先进后出的数据结构,也就是说它们的执行顺序与注册(添加)顺序相反,当执行完所有清理函数后,线程终止。

#include <pthread.h>
void pthread_cleanup_push(void (*routine)(void *), void *arg);
void pthread_cleanup_pop(int execute);

**注意:尽管上面我们将 pthread_cleanup_push()和 pthread_cleanup_pop()称之为函数,但它们是通过宏来实现,

可展开为分别由{和}所包裹的语句序列,push和pop必须成对出现,不然会报错, eg: **

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<string.h>
#include<pthread.h>
void cleanup(void *arg)
{
  printf("cleanup: %s\n", (char *)arg);
}
void *thread_func(void *arg)
{
  pthread_cleanup_push(cleanup, "第一次调用");  // {
  pthread_cleanup_push(cleanup, "第二次调用");  // {
  pthread_cleanup_push(cleanup, "第三次调用");  // {
  pthread_cleanup_pop(0);             // }
  printf("~~~~~~~~~~~~~~~~~~~~\n");
  sleep(2);
  pthread_exit((void *)0);
  //pthread_cleanup_pop(0);
  pthread_cleanup_pop(0);    // }
  pthread_cleanup_pop(0);    // }
}
int main()
{
  int ret = 0;
  pthread_t t;
  void *tret;
  ret = pthread_create(&t, NULL, thread_func, NULL);
  if (ret)
  {
    fprintf(stderr, "pthread_create error: %s\n", strerror(ret));
    exit(-1);
  }
  // 等待线程结束
  ret = pthread_join(t, &tret);
  if (ret)
  {
    fprintf(stderr, "pthread_join error: %s\n", strerror(ret));
    exit(-1);
  }
  printf("新线程终止, code=%ld\n", (long)tret);
  exit(0);
}

8. 线程属性

前面所讲的pthread_create有个参数attr就是线程属性。

8.1 属性初始化

#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr);     // 属性初始化
int pthread_attr_destroy(pthread_attr_t *attr);  // 释放属性

8.2 线程栈属性

#include <pthread.h>
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);
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);
int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr);
int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr);

8.3 分离状态属性

可以在创建线程的时候就设定好分离线程,不需要后面调用pthread_detach来分离

#include <pthread.h>
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);

detachstate:

  • PTHREAD_CREATE_DETACHED:新建线程一开始运行便处于分离状态,以分离状态启动线程,无法被其它线程调用 pthread_join()回收,线程结束后由操作系统收回其所占用的资源;
  • PTHREAD_CREATE_JOINABLE:这是 detachstate 线程属性的默认值,正常启动线程,可以被其它线程获取终止状态信息。


目录
相关文章
|
Linux
linuxC线程同步问题
linuxC线程同步问题
98 0
|
24天前
|
存储 监控 Java
Java多线程优化:提高线程池性能的技巧与实践
Java多线程优化:提高线程池性能的技巧与实践
47 1
|
7天前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
25 15
一个Android App最少有几个线程?实现多线程的方式有哪些?
|
9天前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android应用开发中的多线程编程,涵盖基本概念、常见实现方式及最佳实践。主要内容包括主线程与工作线程的作用、多线程的多种实现方法(如 `Thread`、`HandlerThread`、`Executors` 和 Kotlin 协程),以及如何避免内存泄漏和合理使用线程池。通过有效的多线程管理,可以显著提升应用性能和用户体验。
29 10
|
16天前
|
存储 Ubuntu Linux
C语言 多线程编程(1) 初识线程和条件变量
本文档详细介绍了多线程的概念、相关命令及线程的操作方法。首先解释了线程的定义及其与进程的关系,接着对比了线程与进程的区别。随后介绍了如何在 Linux 系统中使用 `pidstat`、`top` 和 `ps` 命令查看线程信息。文档还探讨了多进程和多线程模式各自的优缺点及适用场景,并详细讲解了如何使用 POSIX 线程库创建、退出、等待和取消线程。此外,还介绍了线程分离的概念和方法,并提供了多个示例代码帮助理解。最后,深入探讨了线程间的通讯机制、互斥锁和条件变量的使用,通过具体示例展示了如何实现生产者与消费者的同步模型。
|
24天前
|
监控 安全 Java
Java多线程调试技巧:如何定位和解决线程安全问题
Java多线程调试技巧:如何定位和解决线程安全问题
71 2
【多线程面试题 一】、 创建线程有哪几种方式?
创建线程的三种方式包括继承Thread类、实现Runnable接口和实现Callable接口,其中Runnable和Callable接口方式更受推荐,因为它们允许多重继承并更好地体现面向对象思想。
|
30天前
|
Java 调度
【多线程面试题 五】、 介绍一下线程的生命周期
线程的生命周期包括新建、就绪、运行、阻塞和死亡状态,线程状态会根据线程的执行情况在这些状态之间转换。
【多线程面试题 五】、 介绍一下线程的生命周期
|
1月前
|
Java
多线程线程同步
多线程的锁有几种方式