LinuxC应用开发学习笔记(十二)—并发(线程)

本文详细讲解了线程概念、创建、终止、同步机制(互斥量、读写锁),包括线程间通信、取消选项、收尸、线程池和同步工具如条件变量、信号量与读写锁的使用实例。深入探讨了线程属性、效率比较、线程与信号、fork的关系,以及多线程应用如求质数和令牌桶算法。
摘要由CSDN通过智能技术生成

线程

1、线程的概念

一个正在运行的函数

线程间通信会比进程间通信简单点。

posix线程是一套标准,而不是实现。

openmp线程

线程标识:pthread_t (理解为一个整型数)

ps下面的 - - 是线程。进程来消耗当前的进程号的。进程就是容器用来承载线程。

pthread_equal();//比较线程标识

pthread_self();//返回当前线程标识

2、线程的创建

pthread_create();
线程的调度取决于调度器调度。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
static void *func(void*p)
{
    puts("thread is working!\n");
    return NULL;
}

int main()
{
    pthread_t  tid;
    int err;
    puts("Begin!\n");
    err = pthread_create(&tid,NULL,func,NULL);
    if (err)
    {
        fprintf(stderr,"pthread_create():%s\n",strerror(err));
        exit(1);
    }
    puts("End!\n");
    exit(0);
}
线程的终止

线程的终止有三种方式:
1)线程从启动例程中返回,返回值为线程的退出码

2)线程可以被同一进程中的其他线程取消

3)线程调用pthread_exit()函数
pthread_join() --> wait(); //收尸

线程的栈清理

//挂钩子函数
pthread_cleanup_push();
//取钩子函数
pthread_cleanup_pop();

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


static void cleanup_func(void *p)
{
    puts(p);
}

static void *func(void*p)
{

    puts("thread is working!\n");

    pthread_cleanup_push(cleanup_func,"cleanup:1");
    pthread_cleanup_push(cleanup_func,"cleanup:2");
    pthread_cleanup_push(cleanup_func,"cleanup:3");

    puts("push over!\n");

    //1表示调用,0表示不调用,成对存在
    pthread_cleanup_pop(1);
    pthread_cleanup_pop(1);
    pthread_cleanup_pop(1);
    
    pthread_exit(NULL);
    //return NULL;

}


int main()
{
    pthread_t  tid;
    int err;

    puts("Begin!\n");

 
    err = pthread_create(&tid,NULL,func,NULL);
    if (err)
    {
        fprintf(stderr,"pthread_create():%s\n",strerror(err));
        exit(1);
    }
    
    pthread_join(tid,NULL);

    puts("End!\n");
    exit(0);
}

多线程实现求质数

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

#define LEFT  30000000
#define RIGHT 30000200
#define THRNUM (RIGHT-LEFT+1) 
static void *thr_prime(void *p);

int main()
{
    int err,i,j,mark;
    pthread_t tid[THRNUM];

    for ( i = LEFT; i <= RIGHT; i++)
    {
        err = pthread_create(tid+(i-LEFT),NULL,thr_prime,(void *)i);
        if (err)
        {
            fprintf(stderr,"pthread_create():%s\n",strerror(err));
            exit(1);
        }
        
    }

    for (size_t i = LEFT; i < RIGHT; i++)
    {
        pthread_join(tid[i-LEFT],NULL);
    }
    
    exit(0);

}


static void *thr_prime(void *p)
{
    int i,j,mark;
    mark = 1;

    i = (int)p;
    
    for (j = 2; j < i/2; j++)
    {
        if (i%j == 0)
        {
            mark = 0;
            break;
        }
    }
    if (mark)
        printf("%d is a primer\n",i);

    pthread_exit(NULL);
}
    

线程的收尸

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

#define LEFT  30000000
#define RIGHT 30000200
#define THRNUM (RIGHT-LEFT+1) 

struct thr_arg_t
{
    int n;
};


static void *thr_prime(void *p);

int main()
{
    int err,i,j,mark;
    pthread_t tid[THRNUM];

    struct thr_arg_t *p;
    void *ptr;

    for ( i = LEFT; i <= RIGHT; i++)
    {

        p = malloc(sizeof(*p));
        if (p == NULL)
        {
            perror("malloc()");
            exit(1);
        }
        
        p->n = i;

        err = pthread_create(tid+(i-LEFT),NULL,thr_prime,p);
        if (err)
        {
            fprintf(stderr,"pthread_create():%s\n",strerror(err));
            exit(1);
        }
        
    }

    for (size_t i = LEFT; i < RIGHT; i++)
    {
        //收尸的时候收回来ptr。
        pthread_join(tid[i-LEFT],&ptr);
        free(ptr);
    }
    
    exit(0);

}


static void *thr_prime(void *p)
{
    int i,j,mark;
    mark = 1;

    i = ((struct thr_arg_t *)p)->n;
    //free(p);
    
    for (j = 2; j < i/2; j++)
    {
        if (i%j == 0)
        {
            mark = 0;
            break;
        }
    }
    if (mark)
        printf("%d is a primer\n",i);

    pthread_exit(p);
}
    

线程的取消选项
线程取消:pthread_cancel();
取消有两种状态:允许和不允许
允许取消又分为:异步取消cancel和推迟取消cancel(默认)–推迟到cancel点再响应。
cancel点: POSIX定义的cancel点都是可能引发阻塞的系统调用。
pthread_setcanceltype();//设置取消方式
pthread_setcancelstate();//设置是否允许取消。
pthread_testcancel():本函数什么都不做,就是一个取消点。

线程分离

pthread_detach();//无法收尸join。

3、线程间同步 :互斥量、读写锁

互斥量:
pthread_mutex_t
//互斥量初始化
pthread_mutex_init();
//互斥量销毁
pthread_mutex_destory();
//加锁
pthread_mutex_lock();
//非阻塞加锁
pthread_mutex_trylock();
//解锁
pthread_mutex_unlock();
//一次性初始化
pthread_once();//只被调用一次

互斥量示例

由于互斥量的存在,导致临界区只允许一个线程访问,避免了线程直接的竞争而导致的数据不准确,最终打印结果是20000000.如果不添加互斥量,最后的结果是一个随机数。

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

static int g_count = 0;

static pthread_mutex_t mut;


static void *thr_handler(void *p)
{
    int i,l_count;;
    int loops = *((int*)p);
   
    for ( i = 0; i < loops; i++)
    {
        pthread_mutex_lock(&mut);  //互斥锁上锁

        l_count = g_count;
        l_count++;
        g_count = l_count;

        pthread_mutex_unlock(&mut);  //互斥锁解锁
        
    }
    
    return (void*)0;
}

static int loops;
int main(int argc,char **argv)
{
    pthread_t tid1,tid2;//线程

    int ret;

    if (argc < 2)
        loops = 10000000;
    else
        loops = atoi(argv[1]);

    pthread_mutex_init(&mut, NULL);

    ret = pthread_create(&tid1,NULL,thr_handler,&loops);
    if (ret)
    {
        perror("pthread_create()");
        exit(1);
    }

    ret = pthread_create(&tid2,NULL,thr_handler,&loops);
    if (ret)
    {
        perror("pthread_create()");
        exit(1);
    }


    ret = pthread_join(tid1,NULL); //等待线程退出
    if (ret)
    {
        fprintf(stderr, "pthread_join error: %s\n", strerror(ret));
        exit(1);
    }

    ret = pthread_join(tid2,NULL); //等待线程退出
    if (ret)
    {
        fprintf(stderr, "pthread_join error: %s\n", strerror(ret));
        exit(1);
    }
    
    printf("g_count = %d\n",g_count);

    exit(0);
}

条件变量示例

条件变量是线程可用的另一种同步机制。条件变量用于自动阻塞线程,直到某个特定事件发生或某个条件满足为止,通常情况下,条件变量是和互斥锁一起搭配使用的。使用条件变量主要包括两个动作:
⚫ 一个线程等待某个条件满足而被阻塞;
⚫ 另一个线程中,条件满足时发出“信号”

条件变量使用 pthread_cond_t 数据类型来表示,类似于互斥锁,在使用条件变量之前必须对其进行初始
化。初始化方式同样也有两种:使用宏 PTHREAD_COND_INITIALIZER 或者使用函数 pthread_cond_init();
条件变量:pthread_con_t
pthread_cond_init();
pthread_cond_destory();
pthread_cond_broadcast();
pthread_cond_signal();
pthread_cond_wait();
pthread_cond_timewait();

对于初始化与销毁操作,有以下问题需要注意:
⚫ 在使用条件变量之前必须对条件变量进行初始化操作,使用 PTHREAD_COND_INITIALIZER 宏或者函数 pthread_cond_init()都行;
⚫ 对已经初始化的条件变量再次进行初始化,将可能会导致未定义行为;
⚫ 对没有进行初始化的条件变量进行销毁,也将可能会导致未定义行为;
⚫ 对某个条件变量而言,仅当没有任何线程等待它时,将其销毁才是最安全的;
⚫ 经 pthread_cond_destroy()销毁的条件变量,可以再次调用 pthread_cond_init()对其进行重新初始化。

信号量: P216 栗子 mysem

读写锁:写锁 互斥锁 读锁 共享锁

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


#define THRNUM 20
#define FNAME "/tmp/out"  
#define LINESIZE 1024

static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;

char linebuf[LINESIZE];

static void  *thr_add(void* p)
{
    FILE *fp;

    fp = fopen(FNAME,"r+");
    if (fp == NULL)
    {
        perror("fopen()");
        exit(1);
    }
    
    //互斥量
    pthread_mutex_lock(&mut);

    fgets(linebuf,LINESIZE,fp);
    fseek(fp,0,SEEK_SET);
    sleep(1);
    fprintf(fp,"%d\n",atoi(linebuf)+1);
    fclose(fp);

    pthread_mutex_unlock(&mut);

    pthread_exit(NULL);

}
int main()
{
    int err,i;
    
    pthread_t tid[THRNUM];

    for ( i = 0; i < THRNUM; i++)
    {
       err =  pthread_create(tid+i,NULL,thr_add,NULL);
       if (err)
       {
           fprintf(stderr,"pthread_create():%sfailed\n",strerror(err));
           exit(1);
       }
       
    }

    for ( i = 0; i < THRNUM; i++)
    {
        pthread_join(tid[i],NULL);
    }
    
    pthread_mutex_destroy(&mut);

    exit(0);
}
创建线程循环打印abcd,3s之后被alarm信号杀死
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>


#define THRNUM 4
#define FNAME "/tmp/out"  
#define LINESIZE 1024

static pthread_mutex_t mut[THRNUM];

char linebuf[LINESIZE];

static int next(int n)
{
    if(n + 1 ==THRNUM)
        return 0;
    return n+1;   
}


static void  *thr_add(void* p)
{
    int n = (int)p;

    int c = 'a' + (int)p;

    while (1)
    {
        pthread_mutex_lock(mut+n);
        write(1,&c,1);
        pthread_mutex_unlock(mut+next(n));
    }
    pthread_exit(NULL);
}


int main()
{
    int err,i;
    
    pthread_t tid[THRNUM];

    for ( i = 0; i < THRNUM; i++)
    {
        pthread_mutex_init(mut+i,NULL);
        pthread_mutex_lock(mut+i);
        err =  pthread_create(tid+i,NULL,thr_add,(void *)i);
        if (err)
        {
            fprintf(stderr,"pthread_create():%sfailed\n",strerror(err));
            exit(1);
        }      
    }

    pthread_mutex_unlock(mut+0);

    alarm(3);

    for ( i = 0; i < THRNUM; i++)
    {
        pthread_join(tid,NULL);
    }
    exit(0);
}

多线程实现令牌桶
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include "mytbf.h"

//static __sighandler_t alarm_handler_save;

static struct mytbf_st* job[MYTBF_MAX];

/*互斥量的初始化*/
static pthread_mutex_t mut_job = PTHREAD_MUTEX_INITIALIZER;

static int inited = 0;

static pthread_t tid_alrm;

static pthread_once_t init_once = PTHREAD_ONCE_INIT;


struct mytbf_st
{
    int cps;//每秒传输的字节数
    int burst;//传输的上限
    int token;//令牌桶
    int pos;//下标
    pthread_mutex_t mut; //互斥量
};

static void *thr_alrm(void *p)
{

    int i;

    //alarm(1);
    while (1)
    {
        pthread_mutex_lock(&mut_job);
        for (i = 0; i < MYTBF_MAX; i++)
        {
            if (job[i] != NULL)
            {
                pthread_mutex_lock(&job[i]->mut);
                job[i]->token += job[i]->cps;
                if (job[i]->token>job[i]->burst)
                    job[i]->token = job[i]->burst;
                pthread_mutex_unlock(&job[i]->mut);
            }
            
        }
        pthread_mutex_unlock(&mut_job);
        sleep(1);
    }
    
    

}

/*模块卸载,保持进出的状态都一致*/
static void module_unload(void)
{
    int i;
    /*恢复功能*/
    pthread_cancel(tid_alrm);
    pthread_join(tid_alrm,NULL);
    for (i = 0; i < MYTBF_MAX; i++)
    {
        if (job[i]!=NULL)
        {
            mytbf_destory(job[i]);
        }
        pthread_mutex_destroy(&mut_job);
        
    }
    
}


static void module_load(void)
{
    //alarm_handler_save = signal(SIGALRM,alarm_handler);
    //alarm(1);



    int err;

    err = pthread_create(&tid_alrm,NULL,thr_alrm,NULL);
    if (err)
    {
        fprintf(stderr,"pthread_create():%s\n",strerror(err));
        exit(1);
    }
    
    atexit(module_unload);
}

static int get_free_pos_unlocked(void)
{
    int i;

    for (i = 0; i < MYTBF_MAX; i++)
    {
        if (job[i] == NULL)
            return i;   
    }
    return -1;
}

static int min(int a,int b)
{
    if (a<b)
        return a;
    return b;
}

mytbf_t *mytbf_init(int cps,int burst)
{
    struct mytbf_st *me;

    int pos;
/*
    if (!inited)
    {
        module_load();
        inited = 1;
    }
*/
    pthread_once(&init_once,module_load);

    me = malloc(sizeof(*me));
    if (me == NULL)
        return NULL;

    me->token = 0;
    me->cps = cps;
    me->burst = burst;
    pthread_mutex_init(&me->mut,NULL);

    pthread_mutex_lock(&mut_job);
    /*临界区函数跳转*/
    pos = get_free_pos_unlocked();
    if (pos < 0)
    {
        pthread_mutex_unlock(&mut_job);
        free(me);
        return NULL;
    }
        
    me->pos = pos;

    job[pos] = me;

    pthread_mutex_unlock(&mut_job);

    return me;

}


/*取令牌*/
int mytbf_fetchtoken(mytbf_t *ptr,int size)
{
    struct mytbf_st *me = ptr;//指针转换

    int n;

    if (size <= 0)
       return -EINVAL;

    pthread_mutex_lock(&me->mut);
    while(me->token <= 0)
    {
        pthread_mutex_unlock(&me->mut);
        sched_yield();
        pthread_mutex_lock(&me->mut);
    }

    n = min(me->token,size);

    me->token -= n;

    pthread_mutex_unlock(&me->mut);

    return n;
    
}

/*归还令牌*/
int mytbf_returntoken(mytbf_t *ptr,int size)
{
    struct mytbf_st *me = ptr;//指针转换

    if (size <= 0)
       return -EINVAL;

    pthread_mutex_lock(&me->mut);
    me->token += size;
    if (me->token > me->burst)
        me->token = me->burst;
    pthread_mutex_unlock(&me->mut);

    return size;     
}


int mytbf_destory(mytbf_t *ptr)
{
    struct mytbf_st *me = ptr;//指针转换

    pthread_mutex_lock(&mut_job);
    job[me->pos] = NULL;
    pthread_mutex_unlock(&mut_job);

    pthread_mutex_destroy(&me->mut);
    free(ptr);

    return 0; 
}

信号量: P216 栗子 mysem
#ifndef MYSEM_H_
#define MYSEM_H_


typedef void mysem_t;

mysem_t *mysem_init(int initval);

int mysem_add(mysem_t *,int);

int mysem_sub(mysem_t *,int);

int mysem_destory(mysem_t *);

#endif // !MYSEM_H_

#include "mysem.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>

struct mysem_st
{
    int value;
    pthread_mutex_t mut;
    pthread_cond_t cond;
};


mysem_t *mysem_init(int initval)
{
    struct mysem_st *me;
    me = malloc(sizeof(*me));
    if (me == NULL)
        return NULL;
    me->value = initval;
    pthread_mutex_init(&me->mut,NULL);
    pthread_cond_init(&me->cond,NULL);
    return me;
    
}

int mysem_add(mysem_t *ptr,int n)
{
    struct mysem_st *me = ptr;

    pthread_mutex_lock(&me->mut);

    me->value += n;

    pthread_cond_broadcast(&me->cond);

    pthread_mutex_unlock(&me->mut);

    return n;
}

int mysem_sub(mysem_t *ptr,int n)
{
    struct mysem_st *me = ptr;
    
    pthread_mutex_lock(&me->mut);
    while (me->value < n)
        pthread_cond_wait(&me->cond,&me->mut);

    me->value -= n;
    pthread_mutex_unlock(&me->mut);

    return n;
    
}

int mysem_destory(mysem_t *ptr)
{
    struct  mysem_st *me = ptr;
    
    pthread_mutex_destroy(&me->mut);

    pthread_cond_destroy(&me->cond);
    
    free(me);
    
}

通知和等待条件变量

条件变量的主要操作便是发送信号(signal)和等待。发送信号操作即是通知一个或多个处于等待状态
的线程,某个共享变量的状态已经改变,这些处于等待状态的线程收到通知之后便会被唤醒,唤醒之后再检
查条件是否满足。等待操作是指在收到一个通知前一直处于阻塞状态。
函数 pthread_cond_signal()和 pthread_cond_broadcast()均可向指定的条件变量发送信号,通知一个或多
个处于等待状态的线程。调用 pthread_cond_wait()函数是线程阻塞,直到收到条件变量的通知。

4、线程相关的属性、线程同步的属性

pthread_attr_init();
pthread_attr_destory();
pthread_attr_setstacksize(&attr,1024*1024);
手册的 man pthread_attr_init(); 的see also
进程与线程 的效率问题 P216

线程同步的属性
互斥量属性:
pthread_mutexattr_init();
pthread_mutexattr_destory();
pthread_mutexattr_getpshared();对互斥量是否跨进程起作用设置属性。
pthread_mutexattr_setpshared();
clone();
pthread_mutexattr_gettype();
pthread_mutexattr_settype();

条件变量:pthread_condattr_init();
pthread_condattr_dsetory();
手册的 pthread_condattr_init(); 的see also
读写锁属性:

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

void *func(void *p)
{
    while (1)
        pause();    
}

int main()
{
    int err,i;
    pthread_t tid;
    pthread_attr_t attr; 
    pthread_attr_init(&attr);  
    pthread_attr_setstacksize(&attr,1024*1024);

    for (i = 0;; i++)
    {
        err = pthread_create(&tid,NULL,func,NULL);
        if (err)
        {
            fprintf(stderr,"pthread_create()%s failed\n",strerror(err));
            break;
        }
    }
    
   printf("%d\n",i);

   pthread_attr_destroy(&attr);

   exit(0); 
}

5、重入、线程和信号的关系、线程与fork的关系

多线程中的IO。

线程和信号的关系

pthread_sigmasl();
sigwait();
线程发信号 pthread_kill

线程与fork的关系

openmp --> www.openmp.org参考内容。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值