linux多线程pthread系列函数详解
- 为什么要引入线程
线程技术早在60年代就被提出,但是在80年代才真正使用到操作系统中。传统UNIX也支持多线程概念,但在一个进程中只允许有一个线程,这样多线程就意味着多进程。现在多线程技术已经被很多操作系统支持,包含Windows/NT,当然也包含Linux。
我们知道新建立一个进程的代价是非常昂贵的,内核需要分配一个新的地址空间,建立众多的数据表来维护他的数据段/代码段等。但是在一个进程中的多个线程,使用相同的地址空间,共享大部分数据,新建立一个线程花费的时间要远远小于新建一个进程,而且,线程之间的切换速度也远远小于进程的切换速度。
另外一点是通信的快速,进程由于有独立的地址空间,进程的数据传递往往使用通信的方式。而线程共享同一个数据空间,多个线程之间只需要做好数据保护,就可以直接使用数据,避免拷贝。
线程优点总结以下几个方面:
1)提高程序响应速度。比如按键响应这种耗时的操作可以在一个新建立的线程中去做,这样就不会影响其他的程序执行。
2)改善程序结构,复杂的逻辑可以按照业务拆分出多个线程处理,程序会利于修改与整理。
3)更好的应用于SMP系统,一个进程的多个线程可以分配到不同CPU上面运行。
相关API
pthread_create()
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
返回值
成功后,pthread_create()返回0;出现错误时,它返回一个错误号,以及的内容线程未定义。
错误码:
EAGAIN 资源不足,无法创建另一个线程,或者系统对数量施加了限制遇到个线程。后一种情况可能以两种方式发生:RLIMIT_NPROC软资源limit(通过setrlimit(2)设置),它限制了真实用户ID的进程数量达到;或者内核对线程数量的系统范围限制,/proc/sys/kernel/threads-max。
EINVAL 属性中的设置无效。
EPERM 没有设置属性中指定的调度策略和参数的权限。
pthread_exit()
void pthread_exit(void *retval);
pthread_join()
功能:阻塞当前线程,直到另一个线程执行结束
int pthread_join(pthread_t thread, void **retval);
返回值
成功后,pthread_join()返回 0
EDEADLK 检测到死锁(例如,两个线程试图相互连接);或线程指定调用线程。
EINVAL 线程不是可连接的线程。
EINVAL 另一个线程已在等待加入此线程。
ESRCH 找不到ID为的线程。
pthread_attr_init()
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
属性有很多,直接看下面的例子:
下面代码的执行效果:
Thread attributes:
Detach state = PTHREAD_CREATE_JOINABLE
Scope = PTHREAD_SCOPE_SYSTEM
Inherit scheduler = PTHREAD_INHERIT_SCHED
Scheduling policy = SCHED_OTHER
Scheduling priority = 0
Guard size = 4096 bytes
Stack address = 0x7f95091ea000
Stack size = 0x801000 bytes
/*gcc main.c -lpthread -o m*/
#define _GNU_SOURCE /* To get pthread_getattr_np() declaration */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
static void display_pthread_attr(pthread_attr_t *attr, char *prefix)
{
int s, i;
size_t v;
void *stkaddr;
struct sched_param sp;
s = pthread_attr_getdetachstate(attr, &i);
if (s != 0)
handle_error_en(s, "pthread_attr_getdetachstate");
printf("%sDetach state = %s\n", prefix,
(i == PTHREAD_CREATE_DETACHED) ? "PTHREAD_CREATE_DETACHED" :
(i == PTHREAD_CREATE_JOINABLE) ? "PTHREAD_CREATE_JOINABLE" :
"???");
s = pthread_attr_getscope(attr, &i);
if (s != 0)
handle_error_en(s, "pthread_attr_getscope");
printf("%sScope = %s\n", prefix,
(i == PTHREAD_SCOPE_SYSTEM) ? "PTHREAD_SCOPE_SYSTEM" :
(i == PTHREAD_SCOPE_PROCESS) ? "PTHREAD_SCOPE_PROCESS" :
"???");
s = pthread_attr_getinheritsched(attr, &i);
if (s != 0)
handle_error_en(s, "pthread_attr_getinheritsched");
printf("%sInherit scheduler = %s\n", prefix,
(i == PTHREAD_INHERIT_SCHED) ? "PTHREAD_INHERIT_SCHED" :
(i == PTHREAD_EXPLICIT_SCHED) ? "PTHREAD_EXPLICIT_SCHED" :
"???");
s = pthread_attr_getschedpolicy(attr, &i);
if (s != 0)
handle_error_en(s, "pthread_attr_getschedpolicy");
printf("%sScheduling policy = %s\n", prefix,
(i == SCHED_OTHER) ? "SCHED_OTHER" :
(i == SCHED_FIFO) ? "SCHED_FIFO" :
(i == SCHED_RR) ? "SCHED_RR" :
"???");
s = pthread_attr_getschedparam(attr, &sp);
if (s != 0)
handle_error_en(s, "pthread_attr_getschedparam");
printf("%sScheduling priority = %d\n", prefix, sp.sched_priority);
s = pthread_attr_getguardsize(attr, &v);
if (s != 0)
handle_error_en(s, "pthread_attr_getguardsize");
printf("%sGuard size = %d bytes\n", prefix, v);
s = pthread_attr_getstack(attr, &stkaddr, &v);
if (s != 0)
handle_error_en(s, "pthread_attr_getstack");
printf("%sStack address = %p\n", prefix, stkaddr);
printf("%sStack size = 0x%x bytes\n", prefix, v);
}
static void * thread_start(void *arg)
{
int s;
pthread_attr_t gattr;
/* pthread_getattr_np() is a non-standard GNU extension that
retrieves the attributes of the thread specified in its
first argument */
s = pthread_getattr_np(pthread_self(), &gattr);
if (s != 0)
handle_error_en(s, "pthread_getattr_np");
printf("Thread attributes:\n");
display_pthread_attr(&gattr, "\t");
exit(EXIT_SUCCESS); /* Terminate all threads */
}
int main(int argc, char *argv[])
{
pthread_t thr;
pthread_attr_t attr;
pthread_attr_t *attrp; /* NULL or &attr */
int s;
attrp = NULL;
/* If a command-line argument was supplied, use it to set the
stack-size attribute and set a few other thread attributes,
and set attrp pointing to thread attributes object */
if (argc > 1) {
int stack_size;
void *sp;
attrp = &attr;
s = pthread_attr_init(&attr);
if (s != 0)
handle_error_en(s, "pthread_attr_init");
s = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (s != 0)
handle_error_en(s, "pthread_attr_setdetachstate");
s = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
if (s != 0)
handle_error_en(s, "pthread_attr_setinheritsched");
stack_size = strtoul(argv[1], NULL, 0);
s = posix_memalign(&sp, sysconf(_SC_PAGESIZE), stack_size);
if (s != 0)
handle_error_en(s, "posix_memalign");
printf("posix_memalign() allocated at %p\n", sp);
s = pthread_attr_setstack(&attr, sp, stack_size);
if (s != 0)
handle_error_en(s, "pthread_attr_setstack");
}
s = pthread_create(&thr, attrp, &thread_start, NULL);
if (s != 0)
handle_error_en(s, "pthread_create");
if (attrp != NULL) {
s = pthread_attr_destroy(attrp);
if (s != 0)
handle_error_en(s, "pthread_attr_destroy");
}
pause(); /* Terminates when other thread calls exit() */
}
pthread_kill()
给指定线程发送信号
int pthread_kill(pthread_t thread, int sig);
返回值
成功后,pthread_kill()返回0;出现错误时,它返回一个错误编号,并且不发送任何信号。
错误:
EINVAL — 指定了无效信号。
ESRCH — 找不到ID为的线程。
线程同步
pthread_mutex_lock():互斥加锁
pthread_mutex_trylock()
pthread_mutex_unlock():互斥锁解锁
pthread_cond_init():初始化条件变量
pthread_cond_signal():发送信号唤醒进程
pthread_cond_wait():等待条件变量的特殊事件发生
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
/**
* @ 动态初始化一个mutex锁
* @ mutex: 指定要初始化的mutex锁的地址 mutexattr: NULL 默认值
* @ 总是成功返回0
*/
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
/**
* @ 销毁mutex锁,释放锁hold的资源
* @ mutex: 指定要销毁的锁的地址
* @ 成功返回0,失败返回非0的错误码
*/
int pthread_mutex_destroy(pthread_mutex_t *mutex);
/**
* @ 加锁 如果锁被其他线程占用,阻塞等待其他线程解锁,如果锁处于解锁状态,加锁,立即返回
* @ mutex: 指定要加锁的锁的地址
* @ 成功返回0,失败返回非0的错误码
*/
int pthread_mutex_lock(pthread_mutex_t *mutex);
/**
* @ 尝试加锁 如果锁处于解锁状态,加锁,立即返回,如果锁被其他线程占用,立即返回错误EBUSY
* @ mutex: 指定要加锁的锁地址
* @ 成功返回0,失败返回非0的错误码
*/
int pthread_mutex_trylock(pthread_mutex_t *mutex);
/**
* @ 解锁
* @ mutex: 指定锁的地址
* @ 成功返回0,失败返回非0的错误码
*/
int pthread_mutex_unlock(pthread_mutex_t *mutex);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);