linux系统编程04-并发:线程

介绍

Untitled

1. 线程的概念

会话→进程组→进程→线程:线程已成为编写程序的基本单位,其他都是容器

  • 一个正在运行的函数,没有主次之分
  • 先有标准,后有实现:Posix线程是一套标准,而不是实现
  • 线程标识: pthread_t 【整型/结构体/共用体,不同标准实现方式不同,posix是整型】

ps axm :查看more,看到线程

ps ax -L :以linux方式查看,可以发现线程是占用进程号的,即也是用进程号标识

Untitled

#include <pthread.h>
//比较两个线程id的大小
int pthread_equal(pthread_t t1, pthread_t t2);
//返回当前线程的id,【类进程的getpid()】
pthread_t pthread_self(void);
**Compile and link with -pthread.**

Untitled

makefile写法:

CFLAGS+=-pthread
LDFLAGS+=-pthread

2. 进程的基本行为

创建:pthread_create

线程的调度也是取决于调取器的策略

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                  void *(*start_routine) (void *), void *arg);
  • 参数:
    • thread :线程号,回填
    • attr :线程的属性,一般默认
    • start_routine :线程跑的函数(线程就是一个正在运行的函数)
    • arg :新线程需要的参数
  • 返回值:
    • 成功:0
    • 失败:返回errno,用 strerror 报错
设置errno 返回errno 报错方式
fprintf
perror
strerror

例子:创建线程

示例代码:

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

static void* func(void *p)
{
    puts("Thread is working!");
    return NULL;
}

int main()
{
    pthread_t tid;
    int err;

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

    exit(0);
}

运行结果:

Untitled

结果分析:

刚创建的线程可能来不及调度,main线程就调用了 exit(0) ,导致整个进程正常终止,回收了所有线程,所以刚创建的线程来不及打印 “Thread is working!”

终止:pthread_exit、pthread_join

进程终止方式:Untitled

Untitled

1)3):正常终止; 2):异常终止

#include <pthread.h>
//线程程终止
void pthread_exit(void *retval);
//线程收尸
int pthread_join(pthread_t thread, void **retval);

示例代码:

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

static void* func(void *p)
{
    puts("Thread is working!");
    pthread_exit(NULL);
    //return NULL;
}

int main()
{
    pthread_t tid;
    int err;

    puts("Begin!");
    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!");

    exit(0);
}

运行结果:

Untitled

对比:

进程 线程
fork() pthread_create()
exec() pthread_create()
wait() pthread_join()
exit() pthread_exit()

exitreturn 的区别,是否做善后,如调用钩子函数/清理线程栈

清理:pthread_cleanup

钩子函数,在执行 pexit 之前会先被执行

#include <pthread.h>
void pthread_cleanup_push(void (*routine)(void *),
                         void *arg);
void pthread_cleanup_pop(int execute);
  • 实际上是宏
    push的另外一个大括号在pop中,必须成对使用,否则报语法错

    Untitled

    Untitled

    可以执行不到,默认为1,但一定要有

    Untitled

示例代码:

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

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

static void* func(void *p)
{
    puts("Thread is working!");
    pthread_cleanup_push(cleanup_func, "cleanup:1");
    pthread_cleanup_push(cleanup_func, "cleanup:2");
    pthread_cleanup_push(cleanup_func, "cleanup:3");

    pthread_cleanup_pop(1);
    pthread_cleanup_pop(0);
    pthread_cleanup_pop(1);

    pthread_exit(NULL);
}

int main()
{
    pthread_t tid;
    int err;

    puts("Begin!");
    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!");

    exit(0);
}

运行结果:

Untitled

取消:pthread_cancel

很常用,比如搜索一个二叉树,四个线程搜索四棵子树,其中一棵找到其他线程就能停了,取消然后收尸

Untitled

pthread_cancel

使某个线程终止,默认是允许在cancel点终止,Posix中cancel是阻塞的系统调用。

Untitled

cancel实际上不会在 cleanup取消,因为不是cancel点,open是

Untitled

线程竞争实例:筛质数

例子:筛素数,每个线程处理一个数

E1:有参数冲突

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

##define LEFT  30000000
##define RIGHT 30000200
##define THRDNUM (RIGHT-LEFT+1)
static void* thr_prim(void *p);
int main()
{
    int i,err;
    pthread_t tid[THRDNUM];

    for(i = LEFT; i <= RIGHT; i++)
    {
      err = pthread_create(tid+i-LEFT, NULL, thr_prim, &i);
      if(err)
      {
          fprintf(stderr, "thread_create():%s\n", strerror(err));
          exit(1);
      }
    }
    
    for(i = LEFT; i <= RIGHT; i++)
        pthread_join(tid[i-LEFT],NULL);

    exit(0);
}
static void *thr_prim(void *p)
{
    int i,j;
    int 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);
    //sleep(1000);
    pthread_exit(NULL);
}

运行结果:

Untitled

问题:

线程创建的时候传的是地址 &i ,相当于 THRDNUM 个线程都在竞争这一个地址上的数据

E2:解决参数冲突

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

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

struct thr_arg_st
{
    int n;
};

static void* thr_prim(void *p);
int main()
{
    int i,err;
    pthread_t tid[THRDNUM];
    struct thr_arg_st *p;
    void *ptr;

    for(i = LEFT; i <= RIGHT; i++)
    {
      //做成结构体传参,malloc申请空间,防止冲突
      p = malloc(sizeof(struct thr_arg_st));
      p->n = i;

      err = pthread_create(tid+i-LEFT, NULL, thr_prim, p);
      if(err)
      {
          fprintf(stderr, "thread_create():%s\n", strerror(err));
          exit(1);
      }
    }
    
    for(i = LEFT; i <= RIGHT; i++)
    {
        //利用返回值free
        pthread_join(tid[i-LEFT],&ptr);
        free(ptr);
    }
    exit(0);
}
static void *thr_prim(void *p)
{
    int i,j;
    int mark = 1;

    i = ((struct thr_arg_st *)p)->n;

    for(j = 2; j < i/2; j++)
    {
        if(i%j == 0)
        {
            mark = 0;
            break;
        }
    }
    if(mark)
        printf("%d is a primer!\n", i);
    //把malloc的地址传回去释放
    pthread_exit(p);
}

运行结果:

Untitled

3. 线程的同步:互斥量、条件变量

(1)互斥量:pthread_mutex_init

安装 posix man手册: sudo apt-get install manpages-posix-dev

Untitled

  • 锁的是临界区(代码),而不是资源本身
  • lock 是 死等,阻塞, trylock 是非阻塞

例子:创建20个线程,分别读取同一个文件,然后+1放回

示例代码:

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

##define THRDNUM     20
##define FILENAME    "/tmp/out"
##define LINESIZE     1024

//创建互斥量:静态初始化
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

static void* thr_add(void *p)
{
    FILE *fp;
    char linebuf[LINESIZE];

    fp = fopen(FILENAME, "r+");
    if(fp == NULL)
    {
        perror("fopen()");
        exit(1);
    }

    //进入临界区
    pthread_mutex_lock(&mutex);
    fgets(linebuf, LINESIZE, fp);
    fseek(fp, 0, SEEK_SET);
    //sleep(1); //有冲突则会强化
    fprintf(fp, "%d\n",atoi(linebuf)+1);
    fclose(fp);
    //出临界区
    pthread_mutex_unlock(&mutex);

    pthread_exit(NULL);
}

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

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

    //销毁信号量
    pthread_mutex_destroy(&mutex);

    exit(0);
}

运行结果:

Untitled

注意点:读完后,要覆盖写掉读的内容,移动文件指针,如果不移:

一直读1,写2,写了20次

Untitled

例子:创建四个线程,彼此同步,打印 “abcd”

示例代码:

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

##define THRDNUM     4

pthread_mutex_t mutex_arr[THRDNUM];

static void* thr_func(void* p)
{
    int n = (int)p;
    int c = 'a'+n;
    
    while(1)
    {
        pthread_mutex_lock(mutex_arr+n);
        write(1,&c,1);
        pthread_mutex_unlock(mutex_arr+(n+1)%THRDNUM);
    }
    pthread_exit(NULL);
}

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

    for(i = 0; i < THRDNUM; i++)
    {
        pthread_mutex_init(mutex_arr+i,NULL);
        pthread_mutex_lock(mutex_arr+i);
        
        err = pthread_create(tid+i, NULL, thr_func, (void*)i);
        if(err)
        {
            fprintf(stderr, "pthread_create():%s\n", strerror(err));
            exit(1);
        }
    }
    
    pthread_mutex_unlock(mutex_arr+0);

    alarm(3);

    for(i = 0; i < THRDNUM; i++)
    {
        pthread_join(tid[i], NULL);
    }

    exit(0);
}

运行结果:

Untitled

思路:

Untitled

刚创建时加锁,使得创建出的四个线程进入 while 后阻塞在锁上,main线程 解开 thread1 的锁,然后开始线程同步。

E3:筛质数池类写法

非正规,任务池

Untitled

main线程下发任务,其他线程处理任务,彼此间通过全局变量 num 通信

  • num > 0 发放任务
  • num == 0 任务以分配,main还未下达
  • num == -1 退出

示例代码:

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

##define LEFT  30000000
##define RIGHT 30000200
##define THRDNUM 4

static int num = 0;
static pthread_mutex_t mutex_num = PTHREAD_MUTEX_INITIALIZER;

static void* thr_prim(void *p);
int main()
{
    int i,err;
    pthread_t tid[THRDNUM];
    //创建线程
    for(i = 0; i < THRDNUM; i++)
    {
      err = pthread_create(tid+i, NULL, thr_prim, (void*)i);
      if(err)
      {
          fprintf(stderr, "thread_create():%s\n", strerror(err));
          exit(1);
      }
    }
    //下发任务
    for(i = LEFT; i <= RIGHT; i++)
    {
        pthread_mutex_lock(&mutex_num);
        while(num != 0)
        {
            pthread_mutex_unlock(&mutex_num);
            sched_yield();
            pthread_mutex_lock(&mutex_num);
        }
        num = i;
        pthread_mutex_unlock(&mutex_num);
    }

    //结束任务
    pthread_mutex_lock(&mutex_num);
    while(num != 0)
    {
        pthread_mutex_unlock(&mutex_num);
        sched_yield();
        pthread_mutex_lock(&mutex_num);
    }
    num = -1;
    pthread_mutex_unlock(&mutex_num);

    for(i = 0; i < THRDNUM; i++)
        pthread_join(tid[i],NULL);

    pthread_mutex_destroy(&mutex_num);
    
    exit(0);
}
static void *thr_prim(void *p)
{
    int i,j;
    while(1)
    {
        int mark = 1;
        pthread_mutex_lock(&mutex_num);
        while(num == 0)
        {
            pthread_mutex_unlock(&mutex_num);
            sched_yield();
            pthread_mutex_lock(&mutex_num);
        }
        if(num == -1)
        {
            //警惕所有跳出临界区的跳转语句,可能导致死锁
            pthread_mutex_unlock(&mutex_num);
            break;
        }

        i = num;
        num = 0;
        pthread_mutex_unlock(&mutex_num);

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

运行结果:

Untitled

注意:

经典死锁:临界区跳转,break, continue, function calling, goTo, long jump

问题:

上下游都忙等,cpu利用率高:上游 main 分配任务后, num 变为0,可是 main 抢不到锁;如果 main 抢不到锁, num==0 ,下游线程又会一直强锁检查,形成恶行循环。一切都取决于调度器能否调度到 main 。或许可以采用通知法的改进,解除忙等。

互斥量令牌桶

修改makefile

CFLAGS+=-pthread
LDFLAGS+=-pthread

all:mytbf

mytbf:main.o mytbf.o
                gcc $^ -o $@ $(CFLAGS) $(LDFLAGS)

clean:
        rm -rf *.o mytbf

示例代码:

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>

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

#include "mytbf.h"

struct mytbf_t
{
    int cps;
    int burst;
    int token;
    int pos;
    //局部互斥量:保护自己的token读写
    pthread_mutex_t mut;
};

//typedef void (*sighandler_t)(int);
//全局互斥量:保护job数组
static struct mytbf_t* job [MYTBF_MAX];
static pthread_mutex_t mut_job = PTHREAD_MUTEX_INITIALIZER;
static pthread_t tid_alrm;

static pthread_once_t init_once = PTHREAD_ONCE_INIT;

static int inited = 0;
//static sighandler_t alarm_handler_save;

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]->cps)
                    job[i]->token = job[i]->cps;
                pthread_mutex_unlock(&job[i]->mut);
            }
        }
        pthread_mutex_unlock(&mut_job);
        sleep(1);
    }
}

//模块卸载:在进程结束的时候调用,钩子函数
static void module_unload(void)
{
    int i;
    //signal(SIGALRM, alarm_handler_save);
    //alarm(0);
    
    //回收线程
    pthread_cancel(tid_alrm);
    pthread_join(tid_alrm, NULL);

    for(i = 0; i < MYTBF_MAX; i++)
        if(job[i]!=NULL)
            mytbf_destroy(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;
}

mytbf_t *mytbf_init(int cps, int burst)
{
    struct mytbf_t *me;
    int pos;
    
    /*lock
    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;
    me->pos = pos;
    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;
    }
    job[pos] = me;
    pthread_mutex_unlock(&mut_job);

    return me;
}

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

int mytbf_fetchtoken(mytbf_t *ptr, int size)
{
    struct mytbf_t *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_t *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_destroy(mytbf_t *ptr)
{
    struct mytbf_t *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;
}

运行结果:

Untitled

存在问题:

fetchtoken 仍是查询法,存在忙等

(2)条件变量:pthread_cond_init

思路:mutex+队列,用来解决互斥量的忙等现象,和互斥量配合使用

  1. 加锁,在connd上等待,解锁

  2. 经典用法:

    mutex+cond = semphor

    mutex+cond = semphor

pthread_cond_wait先解锁后等待,被唤醒后再加锁查看内容

while 是为了防止复用 cond 的时候,无关线程被误唤醒

Untitled

Untitled

Untitled

条件变量令牌桶

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>

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

#include "mytbf.h"

struct mytbf_t
{
    int cps;
    int burst;
    int token;
    int pos;
    //局部互斥锁:用来实现条件变量
    pthread_mutex_t mut;
    //条件变量:保护自己的token读写
    pthread_cond_t cond;
};

//全局互斥量:保护job数组
static struct mytbf_t* job [MYTBF_MAX];
static pthread_mutex_t mut_job = PTHREAD_MUTEX_INITIALIZER;
static pthread_t tid_alrm;

static pthread_once_t init_once = PTHREAD_ONCE_INIT;

static int inited = 0;

static void* thr_alrm(void *p)
{
    int i;

    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]->cps)
                    job[i]->token = job[i]->cps;
                //唤醒阻塞在条件变量上的线程
                pthread_cond_broadcast(&job[i]->cond);
                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_destroy(job[i]);
    pthread_mutex_destroy(&mut_job);
}

static void module_load(void)
{
    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;
}

mytbf_t *mytbf_init(int cps, int burst)
{
    struct mytbf_t *me;
    int pos;
    
    /*lock
    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;
    me->pos = pos;
    pthread_mutex_init(&me->mut,NULL);
    pthread_cond_init(&me->cond,NULL);

    //临界区:注意跳转语句
    pthread_mutex_lock(&mut_job);
    pos = get_free_pos_unlocked();
    if(pos < 0)
    {    
        pthread_mutex_unlock(&mut_job);
        free(me);
        return NULL;
    }
    job[pos] = me;
    pthread_mutex_unlock(&mut_job);

    return me;
}

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

int mytbf_fetchtoken(mytbf_t *ptr, int size)
{
    struct mytbf_t *me = ptr;
    int n;
    if(size < 0)
        return -EINVAL;
    //私有互斥量保护:临界区
    pthread_mutex_lock(&me->mut);
    //条件变量避免忙等
    while(me->token <= 0)
    {
        pthread_cond_wait(&me->cond, &me->mut);
        /*
        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_t *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_cond_broadcast(&me->cond);//并发共用信号量时,你返回的token可能够别人申请的token
    pthread_mutex_unlock(&me->mut);

    return size;
}

int mytbf_destroy(mytbf_t *ptr)
{
    struct mytbf_t *me = ptr;

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

    pthread_mutex_destroy(&me->mut);
    pthread_cond_destroy(&me->cond);
    free(ptr);
    
   return 0;
}

fetchtoken() :不忙等

思路:加入cond,忙等的死循环用wait替代,在适当的地方(等待x就在改变x的地方)唤醒

条件变量筛质数

非忙等

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

##define LEFT  30000000
##define RIGHT 30000200
##define THRDNUM 4

static int num = 0;
static pthread_mutex_t mutex_num = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond_num = PTHREAD_COND_INITIALIZER;

static void* thr_prim(void *p);
int main()
{
    int i,err;
    pthread_t tid[THRDNUM];
    //创建线程
    for(i = 0; i < THRDNUM; i++)
    {
      err = pthread_create(tid+i, NULL, thr_prim, (void*)i);
      if(err)
      {
          fprintf(stderr, "thread_create():%s\n", strerror(err));
          exit(1);
      }
    }
    //下发任务
    for(i = LEFT; i <= RIGHT; i++)
    {
        pthread_mutex_lock(&mutex_num);
        while(num != 0)//在num=0的地方broadcast
        {
            pthread_cond_wait(&cond_num, &mutex_num);
            /*
            pthread_mutex_unlock(&mutex_num);
            sched_yield();
            pthread_mutex_lock(&mutex_num);*/
        }
        num = i;
        pthread_cond_signal(&cond_num);
        pthread_mutex_unlock(&mutex_num);
    }

    //结束任务
    pthread_mutex_lock(&mutex_num);
    while(num != 0)
    {
        pthread_mutex_unlock(&mutex_num);
        sched_yield();
        pthread_mutex_lock(&mutex_num);
    }
    num = -1;
    pthread_cond_broadcast(&cond_num);
    pthread_mutex_unlock(&mutex_num);

    for(i = 0; i < THRDNUM; i++)
        pthread_join(tid[i],NULL);

    pthread_mutex_destroy(&mutex_num);
    
    exit(0);
}
static void *thr_prim(void *p)
{
    int i,j;
    while(1)
    {
        int mark = 1;
        pthread_mutex_lock(&mutex_num);
        while(num == 0)//在使num!=0的地方叫醒
        {
            pthread_cond_wait(&cond_num, &mutex_num);
            /*
            pthread_mutex_unlock(&mutex_num);
            sched_yield();
            pthread_mutex_lock(&mutex_num);*/
        }
        if(num == -1)
        {
            //警惕所有跳出临界区的跳转语句,可能导致死锁
            pthread_mutex_unlock(&mutex_num);
            break;
        }

        i = num;
        num = 0;
        pthread_cond_broadcast(&cond_num);
        pthread_mutex_unlock(&mutex_num);

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

条件变量实现abcd

创建四个线程,彼此同步,打印 “abcd”

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

##define THRDNUM     4

static int num = 0;
static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

static void* thr_func(void* p)
{
    int n = (int)p;
    int c = 'a'+n;

    while(1)
    {
        pthread_mutex_lock(&mut);
        while(num != n)
            pthread_cond_wait(&cond, &mut);
        write(1,&c,1);
        num = (num+1)%THRDNUM;
        pthread_cond_broadcast(&cond);
        pthread_mutex_unlock(&mut);
    }
    pthread_exit(NULL);
}

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

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

    alarm(3);

    for(i = 0; i < THRDNUM; i++)
    {
        pthread_join(tid[i], NULL);
    }

    pthread_mutex_destroy(&mut);
    pthread_cond_destroy(&cond);

    exit(0);
}

(3)信号量:自主实现

方法 构成
互斥量
条件变量 锁+队列
信号量 int+队列

自己实现一个信号量并且封装成库

用封装的库修改筛素数E2,用一批一批的线程计算,每批4个

示例代码:

CFLAGS+=-pthread
LDFLAGS+=-pthread

all:mysem

mysem:main.o mysem.o
                gcc $^ -o $@ $(CFLAGS) $(LDFLAGS)

clean:
        rm -rf *.o mysem
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
#include<unistd.h>
#include"mysem.h"

##define LEFT  30000000
##define RIGHT 30000200
##define THRDNUM (RIGHT-LEFT+1)
##define N     4

//创建信号量
static mysem_st *sem;
static void* thr_prim(void *p);

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

    sem = mysem_init(N);

    if(sem == NULL)
    {
        fprintf(stderr,"mysem_init() failed\n");
        exit(1);
    }

    for(i = LEFT; i <= RIGHT; i++)
    {
      //减资源量,减不动就阻塞在这里
      mysem_sub(sem, 1);
      err = pthread_create(tid+i-LEFT, NULL, thr_prim, (void*)i);
      if(err)
      {
          fprintf(stderr, "thread_create():%s\n", strerror(err));
          exit(1);
      }
    }

    for(i = LEFT; i <= RIGHT; i++)
        pthread_join(tid[i-LEFT],NULL);

    //销毁信号量
    mysem_destroy(sem);

    exit(0);
}
static void *thr_prim(void *p)
{
    int i,j;
    int 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);

   // sleep(5);

    //加资源量,会唤醒
    mysem_add(sem,1);
    pthread_exit(NULL);
}
##ifndef MYSEM_H__
##define MYSEM_H__

typedef void mysem_st;

mysem_st *mysem_init(int initval);
int mysem_add(mysem_st *, int);
int mysem_sub(mysem_st *, int);
int mysem_destroy(mysem_st *);

##endif
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>

#include"mysem.h"

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

mysem_st *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_st *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_st *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_destroy(mysem_st *ptr)
{
    struct mysem_st *me = ptr;

    pthread_mutex_destroy(&me->mut);
    pthread_cond_destroy(&me->cond);
    free(me);

    return 0;
}

运行结果:

main,c 中系上测试用的 sleep(5) 语句, 用 ps ax -L 即可查看每批四个线程的变化

Untitled

拓展:可以变成信号量数组,支持多个信号量,类似于 mytbf (很实用的库模板)

读写锁:

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

4. 线程属性

Untitled

可以设置多少个线程:栈空间/线程占用空间;但是在此之前pid会先被耗尽,所以是受限于pid

Untitled

  • 示例代码:

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    
    int main()
    {
        int i=0;
        pid_t pid;
    
        for(i=0; ;i++)
        {
            pid = fork();
            if(pid < 0)
            {
                perror("fork()");
                break;
            }
            if(pid == 0)
            {
                sleep(3);
                exit(0);
            }
        }
        printf("process num:%d\n",i);
        exit(0);
    }
    
    #include<stdio.h>
    #include<stdlib.h>
    #include<pthread.h>
    #include<string.h>
    #include<unistd.h>
    
    static void* thr_func(void *p)
    {
        while(1)
            pause();
    
        pthread_exit(0);
    }
    
    int main()
    {
        pthread_t tid;
        int err, i;
        pthread_attr_t attr;
    
        pthread_attr_init(&attr);
        pthread_attr_setstacksize(&attr,1024*1024);
    
        for(i=0; ;i++)
        {
            err = pthread_create(&tid, &attr, thr_func, NULL);
            if(err)
            {
                fprintf(stderr,"pthread_create():%s\n",strerror(err));
                break;
            }
        }
        printf("thread number : %d\n",i);
        
        pthread_attr_destroy(&attr);
    
        exit(0);
    }
    

Untitled

Untitled

5.重入

Untitled

线程的几种工作模式

流水线、分治、C/S

流水线、分治、C/S

6. openMP

gcc支持的一种跨语言的语法标记,实现多线程

CFLAGS+=-Wall -fopenmp

几个核则几个线程并发

#include<stdio.h>
#include<stdlib.h>

int main()
{
##pragma omp parallel
    {
        puts("Hello");
        puts("World");
        exit(0);
    }
}

Untitled

分section并发,同样取决于几核

#include<stdio.h>
#include<stdlib.h>
#include<omp.h>

int main()
{
##pragma omp parallel sections
{
##pragma omp section
    printf("[%d]Hello\n",omp_get_thread_num());
##pragma omp section
    printf("[%d]World\n",omp_get_thread_num());
}
    exit(0);
}

Untitled

posted @ 2025-09-17 00:03  Miaops  阅读(8)  评论(0)    收藏  举报