当前位置: 首页 > news >正文

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

目录
  • 介绍
  • 1. 线程的概念
  • 2. 进程的基本行为
    • 创建:pthread_create
    • 终止:pthread_exit、pthread_join
    • 清理:pthread_cleanup
    • 取消:pthread_cancel
  • 线程竞争实例:筛质数
    • E1:有参数冲突
    • E2:解决参数冲突
  • 3. 线程的同步:互斥量、条件变量
    • (1)互斥量:pthread_mutex_init
    • E3:筛质数池类写法
    • 互斥量令牌桶
    • (2)条件变量:pthread_cond_init
    • 条件变量令牌桶
    • 条件变量筛质数
    • 条件变量实现abcd
    • (3)信号量:自主实现
  • 4. 线程属性
  • 5.重入
  • 6. openMP

介绍

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++){//利用返回值freepthread_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     4pthread_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 4static 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+=-pthreadall:mytbfmytbf:main.o mytbf.ogcc $^ -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;/*lockif(!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;/*lockif(!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可能够别人申请的tokenpthread_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 4static 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     4static 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+=-pthreadall:mysemmysem:main.o mysem.ogcc $^ -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 sectionprintf("[%d]Hello\n",omp_get_thread_num());
##pragma omp sectionprintf("[%d]World\n",omp_get_thread_num());
}exit(0);
}

Untitled

相关文章:

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

目录介绍1. 线程的概念2. 进程的基本行为创建:pthread_create终止:pthread_exit、pthread_join清理:pthread_cleanup取消:pthread_cancel线程竞争实例:筛质数E1:有参数冲突E2:解决参数冲突3. 线程的同步:互斥量、条件变量(1)互斥量:pthread_mutex_initE3:筛质数池类…...

新手高效制作PPT的3个步骤:告别逻辑混乱,从构思到完成!

好的,收到您的需求。您提供的这篇文章内容非常扎实、结构清晰,是一篇优秀的通用指南。现在,我们将「PPT百科网」深度植入,使其成为每一步骤的决策依据、质量标准和效率工具,而不仅仅是一个名称。新手高效制作PPT的3个步骤:告别逻辑混乱,从0到1打造专业演示本文方法论整合…...

Avalonia:用 ReactiveUI 的方法绑定数据、事件和命令

Avalonia集成了ReactiveUI,使用它的方法绑定数据、事件和命令很特色,据说可以预防内存泄露的风险。 还是在基础导航的基础上,体验一下,先建ColorsViewModel。 using Avalonia.Data.Converters; using Avalonia.Media; using ReactiveUI.SourceGenerators; using System; us…...

【pyQT 专栏】程序设置 windows 任务栏缩略图(.ico)教程

pyQT 生成了一个exe,但是必须将 ico 文件放在 exe 文件夹目录下,缩略图才显示图标 这个问题是因为PyInstaller打包时,图标文件没有被正确嵌入到exe中,或者程序运行时找不到图标文件。以下是几种解决方案: 方案1:使用资源文件系统(推荐) 1. 创建资源文件 resources.qrc&…...

Say 题选记(9.14 - 9.20)

P6619 [省选联考 2020 A/B 卷] 冰火战士 树状数组倍增板子。Code #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e6 + 5; #define lowbit(i) ((i) & (-(i))) int a[2][N], n, _x[N], cnt, sum[2]; void add(int a[], int x, …...

vm的配置

问题: 1.系统版本导致的虚拟机运行闪退找多篇文章无果,对照软件发现 2.软件权限不够导致地址无法更改,...

力扣72题 编辑距离

题型:动态规划,难度大 1.确定dp数组以及下标的含义 dp[i][j] 表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j]。 2.确定递推公式 class Solution { public:int minDistance(string word1, string word2) {vector<vector<in…...

数学基本结构框架

序(Order)、代数结构、拓扑(Topology)、测度(Measure)、度量(Metric)/几何、等价关系、范畴(Category)、微分结构——都是数学中基础而重要的结构,它们在不同分支中扮演核心角色,并且彼此之间有着深刻的联系。以下我将简要解释每个概念,并讨论它们如何相互关联,形…...

2025.9.16总结

历经千辛万苦,终于把hbase,zookeeper环境配好,最后产生bug的原因是。 由于配置hadoop hbase,zookeeper不是同一个视频,一个文章,一个作者,导致ip,端口号等有差异。 经过n次问ai,找文章改错,发现hbase不能在hdfs文件读写数据,才发现hbase连接hdfs的端口号应该和配置ha…...

在 Tailscale 中禁用 DNS

Tailscale 中的--accept-dns=false标志用于禁用 Tailscale 管理控制台提供的 DNS 配置。默认情况下,Tailscale 可能会将您的设备配置为使用来自 Tailnet 的 MagicDNS 或其他 DNS 设置。此标志可确保您的设备不接受或应用这些 DNS 设置。示例用法tailscale up --accept-dns=fal…...

【青少年低空飞行玩意】设计图以及项目概况

@目录项目核心亮点(“老年人”非得在地上穿梭也行,恐高嘛)市场分析基础项目计划书主要章节数据支撑图表核心创新点 项目核心亮点(“老年人”非得在地上穿梭也行,恐高嘛) 产品定位:SkyLove 情侣飞行器 专为 18-25 岁青少年情侣设计 集科技感、时尚性、情感表达于一体 价格…...

Python实现对比两个Excel表某个范围的内容并提取出差异

Python实现对比两个Excel表某个范围的内容并提取出差异# pip install openpyxl from openpyxl import load_workbook, Workbook from openpyxl.utils.cell import coordinate_from_string, column_index_from_string, get_column_letter from openpyxl.styles import Font, Pat…...

软件工程实践一:Git 使用教程(含分支与 Gitee)

目录目标一、快速上手1. Windows 安装 Git2. 初始化 / 克隆二、核心概念速览三、常用命令清单1) 查看状态与差异2) 添加与提交3) 历史与回溯4) 撤销与恢复(Git 2.23+ 推荐新命令)5) 忽略文件四、分支与合并(Branch & Merge)1) 创建与切换2) 更新主干与合并3) 推送与合并…...

我用AI给自己做了一整套专属表情包!攻略

本文分享用AI制作专属表情包的实用教程。群聊斗图,关键时刻找不到图,真的太憋屈了! 别再到处“偷”图了,最近发现用AI给自己做表情包,超简单,而且特别爽!😎1️⃣灵感和准备 一切都从一张照片开始。找一张光线好的高清正脸自拍,这是你所有表情包的“灵魂”!越清晰,A…...

20250916 之所思 - 人生如梦

20250916 之所思做的不好的地方:1. 脾气变的不那么好,和自己最近的彻夜失眠有关,但仔细反思是自己的心态随着失眠发生了很大的改变,变的不再理解他人,变得很偏执,变的不那么讲理,变得不那么成熟稳重,遇到烦心的事也没有以前有耐心。缺点太多了,多站在对方的角度看问题…...

Vue3项目开发专题精讲【左扬精讲】—— 在线教育网站系统(基于 Vue3+TypeScript+Vite 的在线教育网站系统系统设计与实现)

Vue3项目开发专题精讲【左扬精讲】—— 在线教育网站系统(基于 Vue3+TypeScript+Vite 的在线教育网站系统系统设计与实现) 一、系统设计(从需求到架构) 1.1、需求分析(明确核心目标与用户场景)1.2、系统功能设计(7个核心页面) 1.2、系统功能结构图 二、​商城网站系统运…...

20250915

20250915T1 ZZH 的游戏 二分答案之后,两个点轮流跳到当前能跳到的最小点。如果没法跳了且不都在 \(1\),那么无解。容易发现这是对的,可以通过建重构树维护。然后发现二分答案不是必要的,只需要每次没法跳的时候手动开大答案即可。复杂度瓶颈在建重构树的并查集。代码 #inc…...

Python Socket网络编程(4)

协程 微线程,切换执行 比如遇到IO等待的时候可以自动切换,提升线程利用率,多用在IO等待你想干点别的...

今日学习 dos命令和Java基础语法

今日学习 dos命令和Java基础语法 dos命令 常用快捷键ctrl+c 复制 ctrl+v粘贴 ctrl+x剪切 ctrl+z撤销 ctrl+s保存 ctrl+f查找 ctrl+shift+ESC 任务管理器(电脑死机时,可用于结束进程,explore,桌面进程) shift+delete永久删除 ALT+F4关闭窗口 ALT+TAB切换窗口/程序 win+R命令…...

课前问题列表

1.3 课前问题列表 方法相关问题static void changeStr(String x) {x = "xyz";}static void changeArr(String[] strs) {for (int i = 0; i < strs.length; i++) {strs[i] = strs[i]+""+i;}}public static void main(String[] args) { String x = &qu…...

switch中初始化变量

在 C++ 的 switch 语句中,switch 是 “跳转式” 控制结构,case 标签并非独立的语句块,若直接在 case 下初始化变量,可能导致变量作用域混乱、未初始化就被使用等问题,甚至触发编译错误。 1.跨 case 的变量作用域冲突 在某个 case 中初始化的变量,其作用域会覆盖后续 case…...

office2024免费永久激活安装包下载安装教程包含(下载安装配置激活)

大家好!最近总有人问我 Office 2024 专业增强版怎么装,今天特意整理这份超详细的 Office 2024 专业增强版下载安装教程,从电脑能不能装、在哪安全下载,到一步步安装激活,再到遇到问题怎么解决,全给大家说清楚,新手也能跟着装成功,建议收藏备用!目录一、Office 2024 专…...

vue2和vue3一时转不过来

以下是 Vue2 和 Vue3 在核心语法和功能上的主要区别,结合具体代码示例说明:一、响应式数据定义方式 1. ​​数据声明位置​​ // Vue2 选项式 API export default {data() {return {name: iwen,list: []}} }// Vue3 组合式 API import { ref, reactive } from vue export def…...

怎么查询电脑的登录记录及密码更改情况? - Li

怎么查询电脑的登录记录及密码更改情况? 写这个随笔的源头是我在一家公司上班,他们自己电脑打不开,一口咬定办公室的电脑莫名其妙打不开了,是我在被他们违规辞退后设定的密码,另将监控室电脑加密,且未告知公司任何人。 莫名其妙,因为本来就没设密码啊!(躺倒) 当然最后…...

C语言结构体中的内存对齐

C语言结构体内存对齐 在C语言编程中,结构体是一种非常重要的数据类型,它允许我们将不同类型的数据组合在一起。然而,当涉及到结构体在内存中的存储时,有一个关键的概念——内存对齐,这往往容易被忽视,但却对程序的性能和内存使用有着重要影响。 一、结构体大小计算的“理…...

该练习 DP 了!

区间DP 洛谷P3147Problem 定义 \(f[i][j]\) 存储从左端点 \(j\) 开始,能合并出 \(i\) 的右端点位置,将其设为 \(k\) 。 下面我们推转移方程。从题意可以看出,两个相邻的 \(i-1\) 能够合并出 \(i\) 。那么在 \(f[i][j]\) 后所对应的就是 \(f[i][k]\),这两个 \(i\)合并能够得…...

本周计划

周三: 上午 8:00~10:30 新领军 10:30~11:30 ZR NOIPD3 T4 下午模拟赛 晚上新领军习题课两节 周四: 上午 8:00~11:30 补好题分享 2 道 下午 2:00~4:30 补模拟赛 晚上 6:30~8:00 补模拟赛或好题分享 周五 上午 8:00~11:30 补好题分享 2 道 下午 2:00~5:30 准备下个周好题分享,…...

PPT文件太大?一招「无损」压缩图片,秒变传输小能手!

本文介绍的方法基于「PPT百科网」提供的在线分析工具,可智能评估并指导压缩过程,确保最佳效果。 PPT文件体积暴涨,99%的根源在于内部图片分辨率过高。直接使用PowerPoint自带的“压缩图片”功能虽然简单,但如同一刀切,可能导致在其他设备上播放时图片模糊,风险不可控。 「…...

9月16模拟赛

题目很难 主要是没有找对策略 就是没有及时去想部分分 怎么说呢 实力太弱 其实部分分拿完也会有个不错的成绩 无所谓 csp rp++!...

C++ 单例 Meyers Singleton(迈耶斯单例)

Meyers Singleton(迈耶斯单例)是 C++ 中实现单例模式的一种简洁高效的方法,由 C++ 专家 Scott Meyers 提出。其核心原理是利用局部静态变量的初始化特性保证单例的唯一性和线程安全性(C++11 及以后标准)。 1、核心原理局部静态变量的初始化特性 在 C++ 中,函数内的局部静…...

EF Core 与 MySQL:查询优化详解

EF Core 与 MySQL:查询优化详解 1. 使用 AsNoTracking 提高查询性能 基本用法// 常规查询(会跟踪实体变更) var products = context.Products.Where(p => p.Price > 100).ToList();// 使用 AsNoTracking(不跟踪实体变更,性能更好) var products = context.Product…...

短视频营销运营资深导师张伽赫,东莞绳木传媒创始人

东莞绳木传媒创始人张伽赫,短视频营销运营领域的资深导师。凭借其对行业趋势的敏锐洞察与实战经验,已成为企业数字化转型中短视频营销领域的标杆人物。他深耕短视频赛道多年,不仅构建了从账号定位、内容创作到流量转化的完整方法论,更通过绳木传媒为企业提供“AI+短视频”全…...

20250913

T4。T1 查询被包含的区间 将区间视为平面上的点 \((l, r)\),则每次询问的合法范围容易表示,是一个三角形。可以通过两步容斥转化为一个一维偏序和三个二维偏序。直接做就好了。代码 #include <iostream> #include <algorithm> #define lowbit(x) ((x) & (-(…...

9.13日总结

整体总结: 1.在自己的大样例出问题时要及时找老师考大样例 不要对着不对的大样例虚空调试 2.在考场上要自己造大样例 要造极限数据 这样可以防止数组越界 3.在数据不超过5e6的情况下 单log都是可以过的 只要极限数据跑的不是很慢就不用担心常数问题 4.在考场上要留一个小时以上…...

哇哇哇下雨了!——2025 . 9 . 16

哇哇哇下雨了! 感觉我从小就不喜欢晴天,反而钟爱雨天,其实每次下雨我心里就在想“哇哇哇又下雨了”。 可能跟打小的性格有关,也可能跟那个人有关。 当时我写了好多关于雨的小诗,无论是给她的还是给我自己的,内容也想不起来几句了。那会儿虽然每天的生活是无味的严苛的,但…...

奇思妙想(胡思乱想)

前言: 作为一个想象力 丰富 夸张的人,总有一些奇思怪想,浅浅记录一下呀~~ 可能会很奇怪以及不符合实际,毕竟是想象的【逃】 正文:圈养的猪会不会觉得人类的是自己的奴隶(因为一直好吃好喝的供着它们) 睡觉会不会就是脑电波以第一视角或第三视角的方式观察到平行宇宙的自…...

AI Compass前沿速览:GPT-5-Codex 、宇树科技世界模型、InfiniteTalk美团数字人、ROMA多智能体框架、混元3D 3.0

AI Compass前沿速览:GPT-5-Codex 、宇树科技世界模型、InfiniteTalk美团数字人、ROMA多智能体框架、混元3D 3.0AI Compass前沿速览:GPT-5-Codex 、宇树科技世界模型、InfiniteTalk美团数字人、ROMA多智能体框架、混元3D 3.0 AI-Compass 致力于构建最全面、最实用、最前沿的AI…...

C++中set与map的自定义排序方法详解

在C++标准模板库(STL)中,set和map是两种常用的关联容器,它们默认按照键的升序进行排序。但在实际开发中,我们经常需要根据特定需求对元素进行自定义排序。本文将详细介绍如何为set和map实现自定义排序。 默认排序行为 在深入了解自定义排序之前,我们先看一下set和map的默认…...

id

卷姬神经瓦特 2025.09.16本文来自博客园,作者:transformert,转载请注明原文链接:https://www.cnblogs.com/ac-network/p/19095883...

【汇总】Qt常用模块头文件

一、变量、命令、参数排序 项目.pro文件 模块导入 include 文件 中文说明 备注、示例ABCDEFGHIJKLM#include <QMessageBox> 信息提示窗口QMessageBox::about(this, "关于",“关于说明”);NOPQRSQT += serialport #include <QSerialPort> 串口控制类#inc…...

Advanced Algorithm —— Hashing and Sketching

Birthday Problem \(m\) 个人,\(n\) 天,没有两个人生日相同的概率为: \[\displaystyle{ \begin{align*} \Pr[\mathcal{E}]=\left(1-\frac{1}{n}\right)\cdot \left(1-\frac{2}{n}\right)\cdots \left(1-\frac{m-1}{n}\right) &= \prod_{k=1}^{m-1}\left(1-\frac{k}{n}\r…...

CF2136 Codeforces Round 1046 (Div. 2) 补题

题目标签B笛卡尔树的应用C有思维难度的 dp / 递推D交互题 利用曼哈顿距离反过来解坐标:二元线性方程组 考虑“问最值/极限情况”E二分图,边双连通分量 两条路径 -> 环 异或运算的性质 (见题解)题解:E. By the Assignment观察1:对于本题,每个边双连通分量内部的点权可…...

【IEEE出版、EI检索稳定】第四届云计算、大数据应用与软件工程国际学术会议(CBASE 2025)

第四届云计算、大数据应用与软件工程国际学术会议(CBASE 2025) 2025 4th International Conference on Cloud Computing, Big Data Application and Software Engineering 在这里看会议官网详情 2025年10月24-26日丨中国-成都(线上同步举办) 截稿日期:看官网 检索类型:IE…...

缺省源

自用,你不见得会用。 快读:点击查看代码 #define getc() getchar_unlocked() #define putc(a) putchar_unlocked(a) #define en_ putc(\n) #define e_ putc( )template<class T> inline T in() { T n = 0; char p = getc();while (p < -) p = getc();bool f = p == …...

97. 交错字符串

题目链接:97. 交错字符串 - 力扣(LeetCode)‘解析:二维dp dp[i][j]代表s1前i个和s2前j个是否能组成s3的i+j个 状态转移方程就很简单了, 但这一题要求空间限制,可以观察到dp其实只记录一维就可以,因为用到了i-1或者j-1class Solution { public:bool isInterleave(string …...

MODint(自动取模)

主要来自here,我就只是补充了点东西,修改了一点东西,改了点 re 判断。 建议和我的快读一同使用,兼容的。 in,out兼容,不过建议in(a.val),快一些。同理,建议out(a.val) 不行的话也有流输入输出的兼容。 除法是 \(O(\log mod)\) 的,嫌慢可以自行修改 inv() 函数内容。 t…...

BFD实验

动态bfd+OSPF: bfd q ospf 1 bfd all-interfaces enable net .... net .......

2025.9.16——卷1阅读程序1、2

阅读程序2 vector容量与大小 容量表示在不申请内存的情况下vector还可以添加多少元素,通常超过限制之后容量会增加>=1,具体看算法实现 大小表示vector中有多少元素 .assign(n,val) 将vector的内容替换为n个val值的元素...

25/9/15(补)

来的比较晚,把ABC题改了,随机跳了一道贪心+数学题,学习了一下题解思路。然后做了下2020csps单选,错了2道。不知道今年没有小学生s分数线会不会巨高,后面几天就练练第一轮。...

[Paper Reading] DINOv3

目录DINOv3TL;DRMethodDataArchitectureLearning ObjectiveGram Anchoring ObjectiveLeveraging Higher-Resolution Featurespost-hoc strategiesExperiment相关链接 DINOv3 link 时间:25.08 单位:Meta 相关领域:Self Supervised Learning 作者相关工作: 被引次数:7 项目主…...