线程,有时被称为轻量进程,在我们平常开发中经常会用到,关于线程的介绍网上有很多,在这里我就不再做介绍了。我主要介绍下载Linux我们经常用到的几个与线程有关的函数。
pthread_create
创建线程函数,函数声明为
int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict arg);
返回值
若成功则返回0,否则返回出错编号
参数
第一个参数为指向线程标识符的指针。
第二个参数用来设置线程属性。
第三个参数是线程运行函数的地址。
最后一个参数是运行函数的参数。
伪代码用例
#include <pthread.h>
#include<stdio.h>
void func(void)
{
return;
}
int main(void)
{
pthread_t sp=-1;
pthread_create(&sp, NULL, func, NULL);
return 0;
}
上面伪代码的func是要执行的函数,创建线程后线程就会进入到该函数去运行。但上面的调用还不是很合理。因为创建一个线程默认的状态是joinable, 如果一个线程结束运行但没有被join,则它的状态类似于进程中的Zombie Process,即还有一部分资源没有被回收(退出状态码),创建线程者应该调用pthread_join来等待线程运行结束,并可得到线程的退出代码,回收其资源类似于父进程调用wait,waitpid回收子进程的资源。所以,我们应该在创建了线程后在其后面加入pthread_join。
pthread_join
函数定义
int pthread_join(pthread_t thread, void **retval);
参数
thread: 线程标识符,即线程ID,标识唯一线程。retval: 用户定义的指针,用来存储被等待线程的返回值。
伪代码用例
#include <pthread.h>
#include<stdio.h>
void func(void)
{
return;
}
int main(void)
{
pthread_t sp=-1;
pthread_create(&sp, NULL, func, NULL);
pthread_join(sp,NULL);
return 0;
}
通过调用pthread_join()函数,线程创建者就会在子线程运行结束后回收其资源了。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。pthread_join函数是以阻塞的方式等待thread指定的线程结束的。但是调用pthread_join(pthread_id)后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我们并不希望如此。因此,我们可以用到pthread_detach函数。
pthread_detach
主线程与子线程分离,子线程结束后,资源自动回收。
伪代码用例
#include <pthread.h>
#include<stdio.h>
void func(void)
{
pthread_detach(pthread_self());
pthread_exit(0);
}
int main(void)
{
pthread_t sp=-1;
pthread_create(&sp, NULL, func, NULL);
return 0;
}
除了在子线程中调用pthread_detach(pthread_self()) 我们还可以在主线程中调用 pthread_detach(thread_id)(非阻塞,可立即返回),这将该子线程的状态设置为detached,则该线程运行结束后会自动释放所有资源。
pthread_mutex_init
在多线程编程中,我们往往需要锁住一些变量,避免在自己使用变量的时候有别的线程修改,这时候互斥锁就是一种简单易用的工具了,而pthread_mutex_init()函数就是初始化互斥锁的函数。参数attr指定了新建互斥锁的属性。如果参数attr为空,则使用默认的互斥锁属性,默认属性为快速互斥锁 。
函数声明
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
返回值
成功完成之后会返回零,其他任何返回值都表示出现了错误。
pthread_mutex_destroy
与pthread_mutex_init函数相配对的是pthread_mutex_destroy函数,当使用完互斥锁后将锁销毁。
函数声明
int pthread_mutex_destroy(pthread_mutex_t *mutex); //mutex 指向要销毁的互斥锁的指针
返回值
互斥锁销毁函数在执行成功后返回 0,否则返回错误码。
伪代码用例
#include <pthread.h>
#include <stdio.h>
typedef struct
{
pthread_mutex_t lok;
int count;
}str_st;
int main(void)
{
str_st tmp;
pthread_mutex_init(&tmp.lok,NULL);
pthread_mutex_destroy(&tmp.lok);
return 0;
}
这样就完成了互斥锁的初始化了,但明显只是初始化了互斥锁是不够的,我们仅仅是得到了锁,还需要用钥匙来上锁和解锁,而这我们就需要pthread_mutex_lock()函数和pthread_mutex_unlock()函数来配套使用了。
pthread_mutex_lock
当pthread_mutex_lock()返回时,该互斥锁已被锁定。线程调用该函数让互斥锁上锁,如果该互斥锁已被另一个线程锁定和拥有,则调用该线程将阻塞,直到该互斥锁变为可用为止。
函数声明
int pthread_mutex_lock(pthread_mutex_t *mutex);
返回值
在成功完成之后会返回零,其他任何返回值都表示出现了错误。
pthread_mutex_unlock
pthread_mutex_unlock是可以解除锁定 mutex 所指向的互斥锁的函数。
函数声明
int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值
在成功完成之后会返回零,其他任何返回值都表示出现了错误。
伪代码用例
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
typedef struct
{
pthread_mutex_t lok;
int count;
}str_st;
str_st tmp;
void func1(void)
{
pthread_detach(pthread_self());
pthread_mutex_lock(&tmp.lok);
count++;
pthread_mutex_unlock(&tmp.lok);
pthread_exit(0);
}
void func2(void)
{
pthread_detach(pthread_self());
pthread_mutex_lock(&tmp.lok);
count--;
pthread_mutex_unlock(&tmp.lok);
pthread_exit(0);
}
int main(void)
{
pthread_t sp1=-1;
pthread_t sp2=-1;
pthread_mutex_init(&tmp.lok,NULL);
pthread_create(&sp1, NULL, func1, NULL);
pthread_create(&sp2, NULL, func2, NULL);
sleep(5);
pthread_mutex_destroy(&tmp.lok);
return 0;
}
当线程1调用了pthread_mutex_lock函数上锁后,如果线程2也调用pthread_mutex_lock上锁,那么线程2就会阻塞等待互斥锁解开。但有时候我不想让它阻塞等待,而想让它休眠,这时候就需要加入pthread_cond_t条件变量了。
pthread_cond_t
条件变量是线程同步的一种手段。条件变量用来自动阻塞一个线程,直到条件(predicate)满足被触发为止。通常情况下条件变量和互斥锁同时使用。条件变量使我们可以睡眠等待某种条件出现。而创建和销毁条件边量需要用到pthread_cond_init函数和pthread_cond_destroy函数(类似于上面说的pthread_mutex_init和pthread_mutex_destroy)。
pthread_cond_init
初始化条件变量的函数。
函数声明
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
返回值
函数成功返回0;任何其他返回值都表示错误。
pthread_cond_destroy
销毁条件变量的函数。
函数声明
int pthread_cond_destroy(pthread_cond_t *cond);
参数
cond是指向pthread_cond_t结构的指针。
需要注意的是只有在没有线程在该条件变量上等待时,才可以注销条件变量,否则会返回EBUSY。同时Linux在实现条件变量时,并没有为条件变量分配资源,所以在销毁一个条件变量时,只要注意该变量是否仍有等待线程即可。现在我们创建了条件变量了,但还没使用。使用条件变量一般需要用到pthread_cond_wait函数或者pthread_cond_timedwait函数,无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求。
pthread_cond_wait
条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待”条件变量的条件成立”而挂起;另一个线程使”条件成立”(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。
函数声明
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
参数
cond是指向pthread_cond_t结构的指针,mutex是互斥锁的标识符。
在下面的代码当中可以看到,predicate是用while来检查的而不是用if在做判断。这样做的原因是,pthread_cond_wait的返回并不一定意味着其他线程释放了条件成立信号。
pthread_cond_timedwait
计时等待方式如果在给定时刻前条件没有满足,则返回ETIMEDOUT,结束等待,其中abstime以与time()系统调用相同意义的绝对时间形式出现,0表示格林尼治时间1970年1月1日0时0分0秒。
函数声明
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
参数
cond是指向pthread_cond_t结构的指针,mutex是互斥锁的标识符,abstime为指向timespec结构体的指针。
pthread_cond_signal
有等待函数就有激活函数,而条件变量的激活函数有两个,一个是pthread_cond_signal函数,功能是激活一个等待该条件的线程;另一个pthread_cond_broadcast()则激活所有等待线程。在这里我们只介绍pthread_cond_signal()。
函数声明
int pthread_cond_signal(pthread_cond_t *cond);
参数
cond是指向pthread_cond_t结构的指针。
伪代码用例
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
typedef struct
{
pthread_mutex_t lok;
pthread_cond_t sym;
int count;
}str_st;
str_st tmp;
tmp.count=0;
//该函数增加count的值
void func1(void)
{
pthread_detach(pthread_self());
pthread_mutex_lock(&tmp.lok);
printf("fun1 add lock!\n");
tmp.count++;
printf("count add:%d\n",tmp.count);
if(count > 0) //条件满足时发送信号
{
pthread_cond_signal(&tmp.sym);
}
pthread_mutex_unlock(&tmp.lok);
printf("fun1 release lock!\n");
pthread_exit(0);
}
//该函数减少count的值
void func2(void)
{
pthread_detach(pthread_self());
pthread_mutex_lock(&tmp.lok);
printf("fun2 add lock!\n");
while(count <= 0) //当predicate条件满足时休眠,不满足时结束休眠
{
printf("begin wait\n");
pthread_cond_wait(&tmp.sym,&tmp.lok);//pthread_cond_wait会先解除之前的
//pthread_mutex_lock锁定的tmp.sym,然后阻塞在
//等待队列里休眠,直到再次被唤醒,大多数情况下是
//等待的条件成立而被唤醒,唤醒后,该进程会先锁定
//先pthread_mutex_lock(&mtx);
printf("end wait\n");
}
count--;
printf("count reduce:%d\n",tmp.count);
pthread_mutex_unlock(&tmp.lok);
printf("fun2 release lock!\n");
pthread_exit(0);
}
int main(void)
{
pthread_t sp1=-1;
pthread_t sp2=-1;
pthread_mutex_init(&tmp.lok,NULL);
pthread_cond_init(&tmp.sym,NULL);
pthread_create(&sp1, NULL, func1, NULL);
pthread_create(&sp2, NULL, func2, NULL);
sleep(5);
pthread_cond_destroy(&tmp.sym);
pthread_mutex_destroy(&tmp.lok);
return 0;
}
程序输出
func2 add lock!
begin wait
func1 add lock!
count add:1
func1 release lock!
end wait
count reduce:0
func2 release lock!