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线程同步问题
216 0
|
2月前
|
Java
如何在Java中进行多线程编程
Java多线程编程常用方式包括:继承Thread类、实现Runnable接口、Callable接口(可返回结果)及使用线程池。推荐线程池以提升性能,避免频繁创建线程。结合同步与通信机制,可有效管理并发任务。
174 6
|
5月前
|
Java API 微服务
为什么虚拟线程将改变Java并发编程?
为什么虚拟线程将改变Java并发编程?
316 83
|
2月前
|
Java 调度 数据库
Python threading模块:多线程编程的实战指南
本文深入讲解Python多线程编程,涵盖threading模块的核心用法:线程创建、生命周期、同步机制(锁、信号量、条件变量)、线程通信(队列)、守护线程与线程池应用。结合实战案例,如多线程下载器,帮助开发者提升程序并发性能,适用于I/O密集型任务处理。
299 0
|
3月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
276 16
|
7月前
|
机器学习/深度学习 消息中间件 存储
【高薪程序员必看】万字长文拆解Java并发编程!(9-2):并发工具-线程池
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的强力并发工具-线程池,废话不多说让我们直接开始。
284 0
|
10月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
通过本文,您可以了解如何在业务线程中注册和处理Linux信号。正确处理信号可以提高程序的健壮性和稳定性。希望这些内容能帮助您更好地理解和应用Linux信号处理机制。
206 26
|
10月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
本文详细介绍了如何在Linux中通过在业务线程中注册和处理信号。我们讨论了信号的基本概念,并通过完整的代码示例展示了在业务线程中注册和处理信号的方法。通过正确地使用信号处理机制,可以提高程序的健壮性和响应能力。希望本文能帮助您更好地理解和应用Linux信号处理,提高开发效率和代码质量。
210 17
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
12月前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
712 2

热门文章

最新文章