与线程有关的几个函数pthread_create、pthread_detach、pthread_join、pthread_mutex_lock、pthread_cond_wait等用法

     线程,有时被称为轻量进程,在我们平常开发中经常会用到,关于线程的介绍网上有很多,在这里我就不再做介绍了。我主要介绍下载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!

 

    原文作者:haofeng_ma
    原文地址: https://blog.csdn.net/haofeng_ma/article/details/86747617
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞