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线程同步问题
95 0
|
1天前
|
Java
多线程线程同步
多线程的锁有几种方式
|
9天前
|
调度 Python
|
11天前
|
安全 算法 Java
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)(下)
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)
49 6
|
11天前
|
存储 安全 Java
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)(中)
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)
51 5
|
11天前
|
存储 安全 Java
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)(上)
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)
44 3
|
1天前
|
安全 C# 开发者
【C# 多线程编程陷阱揭秘】:小心!那些让你的程序瞬间崩溃的多线程数据同步异常问题,看完这篇你就能轻松应对!
【8月更文挑战第18天】多线程编程对现代软件开发至关重要,特别是在追求高性能和响应性方面。然而,它也带来了数据同步异常等挑战。本文通过一个简单的计数器示例展示了当多个线程无序地访问共享资源时可能出现的问题,并介绍了如何使用 `lock` 语句来确保线程安全。此外,还提到了其他同步工具如 `Monitor` 和 `Semaphore`,帮助开发者实现更高效的数据同步策略,以达到既保证数据一致性又维持良好性能的目标。
5 0
|
26天前
|
算法 Java 编译器
多线程线程安全问题之系统层面的锁优化有哪些常见的策略
多线程线程安全问题之系统层面的锁优化有哪些常见的策略
|
4天前
|
Java UED
基于SpringBoot自定义线程池实现多线程执行方法,以及多线程之间的协调和同步
这篇文章介绍了在SpringBoot项目中如何自定义线程池来实现多线程执行方法,并探讨了多线程之间的协调和同步问题,提供了相关的示例代码。
29 0
|
26天前
|
Java
多线程线程安全问题之什么是锁的粒度,减少锁的粒度有哪些好处
多线程线程安全问题之什么是锁的粒度,减少锁的粒度有哪些好处