Linux pthread¶
- 注意事项
- 在编译代码时需要加上
-pthread
选项
1 线程¶
1.1 线程创建¶
线程函数一定要声明称`void* fun(void*)`类型
- 线程创建使用函数
pthread_self()
: 获取当前线程 idint 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 可以为 NULLint 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 可为 NULLint 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;