跳转至

Linux pthread

  • 注意事项
  • 在编译代码时需要加上 -pthread 选项

1 线程

1.1 线程创建

线程函数一定要声明称`void* fun(void*)`类型
  • 线程创建使用函数
  • pthread_self(): 获取当前线程 id
  • int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg): 创建一个线程, 线程函数可以是静态类成员函数,可以是普通函数,但不能是类非静态成员函数
  • int pthread_join(pthread_t thread, void **retval): 阻塞等待线程结束,retval 可以接收线程退出时返回的数据
    • retval: 非 NULL 则含有由 pthread_exit 或函数取消携带的返回值
  • int pthread_detach(pthread_t thread): 分离线程,让其后台运行
  • void pthread_exit(void *retval): 终止当前线程
    • retval: 返回的值,可以在 pthread_join 第 2 个参数获取
  • 设置线程创建属性
  • int pthread_attr_init(pthread_attr_t *attr);: 初始化线程创建属性(attr 可有 pthread_attr_ 开头函数设置)
  • int pthread_attr_destroy(pthread_attr_t *attr);: 释放线程创建属性
  • pthread_exit:线程退出,可携带退出数据,pthread_join 中低个参数获取
  • 线程取消函数
  • int pthread_cancel(pthread_t thread): 取消线程
    • thread: 待取消的线程 id
  • int pthread_setcancelstate(int state, int *oldstate): 设置线程是否可以被取消的状态,决定 pthread_cancel 发送的取消线程是否有效
    • state: 新状态,可选值有:PTHREAD_CANCEL_ENABLE(可以被取消,)、PTHREAD_CANCEL_DISABLE(不可以被取消)
    • oldstate: 原状态
  • int pthread_setcanceltype(int type, int *oldtype): 设置线程的取消模式
    • type: 新类型,可选择有:PTHREAD_CANCEL_DEFERRED(延迟取消,在下一个取消点处)、PTHREAD_CANCEL_ASYNCHRONOUS(立即取消)
    • oldtype: 原类型
  • void pthread_testcancel(void): 创建线程取消点,在 pthread_setcanceltype 设置的 PTHREAD_CANCEL_ASYNCHRONOUS 类型以及有线程取消事件时退出线程
  • 示例
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>

#define handle_error_en(en, msg) \
    do                           \
    {                            \
        errno = en;              \
        perror(msg);             \
        exit(EXIT_FAILURE);      \
    } while (0)

#define handle_error(msg)   \
    do                      \
    {                       \
        perror(msg);        \
        exit(EXIT_FAILURE); \
    } while (0)

struct thread_info
{                        /* Used as argument to thread_start() */
    pthread_t thread_id; /* ID returned by pthread_create() */
    int thread_num;      /* Application-defined thread # */
    char *argv_string;   /* From command-line argument */
};

/* Thread start function: display address near top of our stack,
   and return upper-cased copy of argv_string */

static void *
thread_start(void *arg)
{
    struct thread_info *tinfo = arg;
    char *uargv, *p;

    printf("Thread %d: top of stack near %p; argv_string=%s\n",
           tinfo->thread_num, &p, tinfo->argv_string);

    uargv = strdup(tinfo->argv_string);
    if (uargv == NULL)
        handle_error("strdup");

    for (p = uargv; *p != '\0'; p++)
        *p = toupper(*p);

    return uargv;
}

int main(int argc, char *argv[])
{
    int s, tnum, opt, num_threads;
    struct thread_info *tinfo;
    pthread_attr_t attr;
    int stack_size;
    void *res;

    /* The "-s" option specifies a stack size for our threads */

    stack_size = -1;
    while ((opt = getopt(argc, argv, "s:")) != -1)
    {
        switch (opt)
        {
        case 's':
            stack_size = strtoul(optarg, NULL, 0);
            break;

        default:
            fprintf(stderr, "Usage: %s [-s stack-size] arg...\n",
                    argv[0]);
            exit(EXIT_FAILURE);
        }
    }

    num_threads = argc - optind;

    /* Initialize thread creation attributes */

    s = pthread_attr_init(&attr);
    if (s != 0)
        handle_error_en(s, "pthread_attr_init");

    if (stack_size > 0)
    {
        s = pthread_attr_setstacksize(&attr, stack_size);
        if (s != 0)
            handle_error_en(s, "pthread_attr_setstacksize");
    }

    /* Allocate memory for pthread_create() arguments */

    tinfo = calloc(num_threads, sizeof(struct thread_info));
    if (tinfo == NULL)
        handle_error("calloc");

    /* Create one thread for each command-line argument */

    for (tnum = 0; tnum < num_threads; tnum++)
    {
        tinfo[tnum].thread_num = tnum + 1;
        tinfo[tnum].argv_string = argv[optind + tnum];

        /* The pthread_create() call stores the thread ID into
           corresponding element of tinfo[] */

        s = pthread_create(&tinfo[tnum].thread_id, &attr,
                           &thread_start, &tinfo[tnum]);
        if (s != 0)
            handle_error_en(s, "pthread_create");
    }

    /* Destroy the thread attributes object, since it is no
       longer needed */

    s = pthread_attr_destroy(&attr);
    if (s != 0)
        handle_error_en(s, "pthread_attr_destroy");

    /* Now join with each thread, and display its returned value */

    for (tnum = 0; tnum < num_threads; tnum++)
    {
        s = pthread_join(tinfo[tnum].thread_id, &res);
        if (s != 0)
            handle_error_en(s, "pthread_join");

        printf("Joined with thread %d; returned value was %s\n",
               tinfo[tnum].thread_num, (char *)res);
        free(res); /* Free memory allocated by thread */
    }

    free(tinfo);
    exit(EXIT_SUCCESS);
}

1.2 线程属性

2 锁

2.1 互斥量

  • 互斥量:pthread_mutex_t
  • 相关函数
  • int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);: 初始化互斥量,attr 可以为 NULL
  • int pthread_mutex_lock(pthread_mutex_t *mutex);: 加锁
  • int pthread_mutex_trylock(pthread_mutex_t *mutex);: 尝试加锁
  • int thread_mutex_unlock(pthread_mutex_t *mutex);: 解锁
  • int pthread_mutex_destroy(pthread_mutex_t *mutex);: 销毁互斥量

2.2 自旋锁

  • 特点:等待锁时,处于忙等待状态,耗费 cpu,适合在锁占用时间特别短的场景下使用
  • 自旋锁:pthread_spinlock_t
  • 相关函数
  • int pthread_spin_init(pthread_spinlock_t *lock, int pshared);: 初始化自旋锁
  • int pthread_spin_lock(pthread_spinlock_t *lock);: 加自旋锁
  • int pthread_spin_trylock(pthread_spinlock_t *lock);: 尝试加自旋锁
  • int pthread_spin_unlock(pthread_spinlock_t *lock);: 解自旋锁
  • int pthread_spin_destroy(pthread_spinlock_t *lock);: 销毁自旋锁

2.3 条件变量

pthread_cond_wait 需要传入一个已经加锁的mutex,当阻塞时,mutex又被解锁。如果接收到信号,mutex又被加锁,所以pthread_cond_wait之后还需要解锁。
pthread_cond_signal 执行前也需要先先加锁,执行完再解锁
  • 条件变量:pthread_cond_t
  • 相关函数
  • int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);: 初始化条件变量,sttr 可为 NULL
  • int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);: 等待指定的时间,无论是否被唤醒都将返回
  • int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);: 阻塞等待被唤醒
  • int pthread_cond_broadcast(pthread_cond_t *cond);: 唤醒所有阻塞在此 cond 上的线程
  • int pthread_cond_signal(pthread_cond_t *cond);: 唤醒一个阻塞在此 cond 上的线程
  • int pthread_cond_destroy(pthread_cond_t *cond);: 销毁条件变量
  • pthread_cond_wait 示例
pthread_mutex_lock(&mut);

while (!buffer_is_set())
    pthread_cond_wait(&cond, &mut);

consume_buffer();

pthread_mutex_unlock(&mut);
  • pthread_cond_signal 伪代码
 pthread_lock(&mut);

 setting_buffer(); /* Now buffer_is_set() will return true */

 pthread_cond_signal(&cond);

 pthread_unlock(&mut);

2.4 读写锁

https://www.cnblogs.com/dins/p/pthread-rwlock-t.html - 读写锁:pthread_rwlock_t - 相关函数 - int pthread_rwlock_init(pthread_rwlock_t * rwlock, const pthread_rwlockattr_t * attr);: 初始化读写锁 - 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_destroy(pthread_rwlock_t *rwlock);: 销毁读写锁

3 线程名

1. 默认线程名继承自程序名。
2. 线程名不超过16字节,且最后一个字节需要是`\0`
3. pthread_getname_np获取线程名时,传入的buffer需要大于16字节。
int pthread_setname_np(pthread_t thread, const char *name);
int pthread_getname_np(pthread_t thread,
                      char *name, size_t len);

4 CPU 亲和力

/* Set the CPU affinity for a task */
extern int sched_setaffinity (__pid_t __pid, size_t __cpusetsize,
                  const cpu_set_t *__cpuset) __THROW;

/* Get the CPU affinity for a task */
extern int sched_getaffinity (__pid_t __pid, size_t __cpusetsize,
                  cpu_set_t *__cpuset) __THROW;