Linux 线程和线程锁

该博客围绕Linux多线程展开,介绍了线程(pthread_t)的创建、等待、终止方法,以及如何设置线程属性,包括初始化、线程分离、调度策略等。此外,还讲解了线程锁中的互斥锁,涵盖其创建(静态和动态)、操作和销毁,并给出相关例子。

1 线程(pthread_t)

1.1 创建、等待、终止线程

  • 线程创建

    #include <pthread.h>
    
    /**@param  tidp      -新创建的线程ID指向的内存单元。
     * @param  attr      -线程属性,默认为NULL
     * @param  start_rtn -线程函数
     * @param  arg       -线程函数的传入参数
     * @return 0          创建成功
     *         -1         创建失败
     */
    int pthread_create( pthread_t *restrict tidp,   
                        const pthread_attr_t *restrict attr,
                        void *(*start_rtn)(void *),
                        void *restrict arg );
    
  • 等待线程返回

    /**@param   tid       -需要等待的线程(不能是分离线程)
     * @param   status    -线程的返回值
     * @return  0          正常返回
     *          ESRCH      没有找到与给定的线程ID 相对应的线程。
     *          EDEADLK    将出现死锁,如一个线程等待其本身,或者线程A和线程B 互相等待。
     *          EINVAL     与给定的线程ID相对应的线程是分离线程。
     * @return 
     */
    int pthread_join( pthread_t tid, void **status);
    
  • 提前终止线程

    /**@param   rertval   -返回值,提前终止也可以返回值
     * @note    该语句在线程函数里使用,如 pthread_exit(0);   
     */
    void pthread_exit(void  *retval);
    
  • 例子

    #include <stdlib.h>   
    #include <stdio.h>   
    #include <errno.h>   
    #include <pthread.h>   
    
    static void pthread_func_1 (void);   
    static void pthread_func_2 (void);   
    
    int main (int argc, char** argv)   
    {   
        pthread_t      pt_1;   
        pthread_t      pt_2;   
        pthread_attr_t attr;   
        int ret = 0;   
    
        /*初始化属性线程属性*/
        pthread_attr_init(&attr);   
        pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);          // 与系统抢占资源
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // 分离状态
    
        ret = pthread_create (&pt_1, &attr, pthread_func_1, NULL);   // pt_1 设置以上属性
        if (ret != 0) { perror ("pthread_1_create"); }   
    
        ret = pthread_create (&pt_2, NULL, pthread_func_2, NULL);    // pt_2 默认属性
        if (ret != 0) { perror ("pthread_2_create"); }
    
        pthread_join (pt_2, NULL);   // 等待 pt_2 返回
    
        return 0;   
    }   
    
    static void pthread_func_1(void)   
    {   
        for (int i = 0; i < 6; i++) {    
            printf ("This is pthread_1.\n");   
            if (i == 2) {   
              pthread_exit (0);   
            }
        } 
        return ;
    }   
    
    static void pthread_func_2(void)   
    {   
        for (int i = 0; i < 3; i ++) {   
            printf ("This is pthread_2.\n");   
        }   
        return;   
    }  
    

1.2 设置线程属性

在上面的例子中,创建线程函数 pthread_create() 的第二个参数是描述线程的属性,如果为 NULL,则为默认属性,也可以手动设置属性,使用结构体 pthread_attr_t,后面的描述都是配置这个结构体:

typedef struct
{
       int                     detachstate;     // 线程的分离状态
       int                     schedpolicy;     // 线程调度策略
       struct sched_param      schedparam;      // 线程的调度参数
       int                     inheritsched;    // 线程的继承性
       int                     scope;           // 线程的作用域
       size_t                  guardsize;       // 线程栈末尾的警戒缓冲区大小
       int                     stackaddr_set;
       void *                  stackaddr;       // 线程栈的位置
       size_t                  stacksize;       // 线程栈的大小
} pthread_attr_t;
  • 初始化

    #include <pthread.h>
    int pthread_attr_init(pthread_attr_t *attr);    // 初始化
    int pthread_attr_destroy(pthread_attr_t *attr); // 去除初始化
    
  • 线程分离

    #include <pthread.h>
    int pthread_attr_getdetachstate(const pthread_attr_t * attr, int * detachstate);
    int pthread_attr_setdetachstate(pthread_attr_t * attr, int detachstate);
    //参数:attr:线程属性变量
    //     detachstate:分离状态属性   
    //          PTHREAD_CREATE_DETACHED    分离状态启动
    //          PTHREAD_CREATE_JOINABLE    正常启动线程
    //若成功返回0,若失败返回-1。
    
  • 线程的继承性
    继承性决定调度的参数是从创建的进程中继承还是使用在 schedpolicyschedparam 属性中显式设置的调度信息。

    #include <pthread.h>
    int pthread_attr_getinheritsched(const pthread_attr_t *attr,int *inheritsched);
    int pthread_attr_setinheritsched(pthread_attr_t *attr,int inheritsched);
    //参数:
    //attr             线程属性变量
    //inheritsched     线程的继承性
    //    PTHREAD_INHERIT_SCHED: 新的线程继承创建线程的策略和参数!
    //    PTHREAD_EXPLICIT_SCHED:新的线程继承策略和参数来自于schedpolicy和schedparam属性中显示设置的调度信息!
    //若成功返回0,若失败返回-1。
    
  • 调度策略

    int pthread_attr_getschedpolicy(const pthread_attr_t *, int * policy)
    int pthread_attr_setschedpolicy(pthread_attr_*, int policy)
    //参数:
    //attr            线程属性变量
    //policy          调度策略   
    //     SCHED_FIFO     :先进先出
    //     SCHED_RR       :轮转法
    //     SCHED_OTHER    :其他方法,不支持优先级
    //若成功返回0,若失败返回-1。
    
  • 调度参数

    int pthread_attr_getschedparam(const pthread_attr_t *,struct sched_param *);
    int pthread_attr_setschedparam(pthread_attr_t *,const struct sched_param *);
    //参数:
    //        attr            线程变量属性
    //        param        sched_parm 结构体
    //若成功返回0,若失败返回-1。
    
    // 用到的结构体
    // /usr/include/bits/sched.h
    struct sched_param
    {
        int sched_priority;    // 参数的本质就是优先级,大的权值对应高的优先级!
    };
    // 获取可以设置的最大、最小优先级
    #include <pthread.h>
    int sched_get_priority_max( int policy );
    int sched_get_priority_min( int policy );
    //参数:
    // policy     调用策略,也就是说对于不同的策略的值是不一样的!
    // _max:     系统支持的优先级的最大值
    // _min :    系统支持的优先级的最小值
    
  • 线程的作用域
    作用域控制线程是否在进程内或在系统级上竞争资源

    #include <pthread.h>   
    int pthread_attr_getscope( const pthread_attr_t * attr, int * scope );
    int pthread_attr_setscope( pthread_attr_t*, int scope );
    // 参数:
    // attr          线程属性变量
    // scope         线程的作用域       
    //     PTHREAD_SCOPE_PROCESS  (进程内竞争资源)
    //     PTHREAD_SCOPE_SYSTEM   (系统级竞争资源)
    // 若成功返回0,若失败返回-1。
    
  • 线程栈的大小

    int pthread_attr_getstacksize(const pthread_attr_t *,size_t * stacksize);
    int pthread_attr_setstacksize(pthread_attr_t *attr ,size_t *stacksize);
    // 参数:
    // attr             线程属性变量
    // stacksize        堆栈大小
    // 若成功返回0,若失败返回-1。    
    
  • 线程栈的地址

    #include <pthread.h>
    int pthread_attr_getstackaddr(const pthread_attr_t *attr,void **stackaddf);
    int pthread_attr_setstackaddr(pthread_attr_t *attr,void *stackaddr);
    // 参数:attr          线程属性变量
    //      stackaddr     堆栈地址           
    // 若成功返回0,若失败返回-1。
    
  • 警戒缓冲区

    线程属性guardsize控制着线程栈末尾之后以避免栈溢出的扩展内存大小。这个属性默认设置为PAGESIZE个字节。可以把guardsize线程属性设为0,从而不允许属性的这种特征行为发生:在这种情况下不会提供警戒缓存区。同样地,如果对线程属性stackaddr作了修改,系统就会认为我们会自己管理栈,并使警戒栈缓冲区机制无效,等同于把guardsize线程属性设为0。

    #include <pthread.h>                   
    int pthread_attr_getguardsize(const pthread_attr_t *restrict attr,size_t *restrict guardsize);
    int pthread_attr_setguardsize(pthread_attr_t *attr ,size_t *guardsize);
    // 若成功返回0,若失败返回-1。
    

2 线程锁

2.1 互斥锁

  • 创建
    可以静态创建:

    #include <pthread.h>
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    

    可以动态创建:

    #include <pthread.h>
    pthread_mutex_t mutex;
    int pthread_mutex_init(pthread_mutex_t*mutex, const pthread_mutexattr_t * attr);
    // 其中:
    // mutex:所需创建的锁;
    // attr:创建锁的属性。一般默认为NULL,表示线程间共享;		
    
  • 操作

    int pthread_mutex_lock(pthread_mutex_t*mutex);     // 加锁
    int pthread_mutex_trylock(pthread_mutex_t *mutex); // 尝试加锁,成功返回 0,否则返回 -1
    int pthread_mutex_timedlock(pthread_mutex_t *__restrict __mutex, \
         const struct timespec *__restrict __abstime); // 在时间范围内加锁,成功返回 0,否则返回 -1
         
    int pthread_mutex_unlock(pthread_mutex_t *mutex);  // 解锁
    
  • 销毁

    int pthread_mutexattr_destroy (pthread_mutex_t *mutex);
    
  • 例子

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

pthread_mutex_t mutex; //定义锁

void *tfn(void *arg) {

    while (1) {
        pthread_mutex_lock(&mutex); //加锁
        printf("hello ");
        sleep(1); /*模拟长时间操作共享资源,导致cpu易主,产生与时间有关的错误*/
        printf("world\n");
        pthread_mutex_unlock(&mutex); //解锁
        sleep(1);                     //睡眠,释放cpu
    }

    return NULL;
}

int main(void) {
    pthread_t tid;

    pthread_mutex_init(&mutex, NULL); //初始化锁 mutex==1
    pthread_create(&tid, NULL, tfn, NULL);
    while (1) {
        pthread_mutex_lock(&mutex); //加锁

        printf("HELLO ");
        sleep(1);
        printf("WORLD\n");
        pthread_mutex_unlock(&mutex); //解锁

        sleep(1);
    }

    pthread_mutex_destroy(&mutex); //销毁锁

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值