线程同步
解决竞争问题就要用到线程同步,用到互斥量pthread_mutex_t这个机制。
互斥量:相当于有一把琐,当一个线程抢到了这把锁,就把门锁上,然后再操作。出来再解锁,再由其他的线程抢锁。
是限制一段代码,以独占的形式实现.
相关函数:
pthread_mutex_destroy();
pthread_mutex_init();
— destroy and initialize a mutex
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);//动态初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//静态初始化方式,用宏来初始化
如果互斥量是一个变量的话,就用静态比较方便,用的就是默认的属性。
如果更改互斥量的属性或是互斥量位于结构体当中,就要用动态的初始化。
pthread_mutex_lock();
pthread_mutex_trylock();
pthread_mutex_unlock();
— lock and unlock a mutex
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);//阻塞
int pthread_mutex_trylock(pthread_mutex_t *mutex);//非阻塞
int pthread_mutex_unlock(pthread_mutex_t *mutex);
lock和 unlock之间的地方我们通常叫做临界区
互斥量的应用
在下面一段代码中,20个线程,若是一个线程在读的时候,别的线程在写,就会出现错误。所以20个线程不可以同时读,也不可以同时写。关闭文件也必须一个一个做。
同一时候只有一个线程来做的代码叫做临界区,我们需要在临界区前lock住,退出临界区的时候进行unlock。
互斥量限制一段代码的使用。
如下代码,加上临界区的使用。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define THRNUM 20
#define FNAME "/tmp/out"
#define LINESIZE 1024
static pthread_mutex_t mut=PTHREAD_MUTEX_INITIALIZER;
static void * thr_add(void *p)
{
FILE *fp;
char linebuf[LINESIZE];
fp=fopen(FNAME,"r+");
if(fp==NULL)
{
perror("fopen()");
exit(1);
}
pthread_mutex_lock(&mut);
fgets(linebuf,LINESIZE,fp);
fseek(fp,0,SEEK_SET);
fprintf(fp,"%d\n",atoi(linebuf)+1);
fclose(fp);
pthread_mutex_unlock(&mut)
pthread_exit(NULL);
}
int main()
{
pthread_t tid[THRNUM];
int i,err;
for(i=0;i<THRNUM;i++)
{
err=pthread_create(tid+i,NULL,thr_add,NULL);
if(err)
{
fprintf(stderr,"pthread_create():%s\n",strerror(err));
}
}
for(i=0;i<THRNUM;i++)
pthread_join(tid[i],NULL);
exit(0);
}
运行结果为21是正确的
在来个小例子,有四个线程,向终端不停的输出abcd
第一个线程打印a,第二个线程打印b······
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define THRNUM 4
static void *thr_func(void *p)
{
int c='a'+(int)p;
while(1)
write(1,&c,1);
pthread_exit(NULL);
}
int main()
{
int i,err;
pthread_t tid[THRNUM];
for(i=0;i<THRNUM;i++)
{
err=pthread_create(tid+i,NULL,thr_func,(void *)i);
if(err)
{
fprintf(stderr,"pthread_create():%s\n",strerror(err));
exit(1);
}
}
alarm(5);
for(i=0;i<THRNUM;i++)
{
pthread_join(tid[i],NULL);
}
exit(0);
}
alarm(5):当前进程会在五秒钟之后被杀掉。
从结果可以看出没有很强的规律性,乱序输出的。
如果要规律性的输出abcd,abcd······,思路:打印完a,解锁b,打印完b,解锁c,打印完c,解锁d。需要四个1互斥量
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define THRNUM 4
static pthread_mutex_t mut[THRNUM];
static int next(int n)
{
if(n+1==THRNUM)
return 0;
return n+1;
}
static void *thr_func(void *p)
{
int n=(int)p;
int c='a'+n;
while(1)
{
pthread_mutex_lock(mut+n);
write(1,&c,1);
pthread_mutex_unlock(mut+next(n));
}
pthread_exit(NULL);
}
int main()
{
int i,err;
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_func,(void *)i);
if(err)
{
fprintf(stderr,"pthread_create():%s\n",strerror(err));
exit(1);
}
}
pthread_mutex_unlock(mut+0);
alarm(5);
for(i=0;i<THRNUM;i++)
{
pthread_join(tid[i],NULL);
}
exit(0);
注意,加锁或是解锁,针对的是一段代码,而不是变量。
运行结果:
线程池的实现:
筛质数
之前是201个线程对应201个tid
现在做出改变201个线程对应N个
(相当于一个任务池),不是正规的线程池
main向下仍任务,例如把3000000扔到num中,下面的线程开始抢任务,把这个数拿走,然后main再把3000001仍进num,闲着的两个线程就抢这个3000001。
这里用num>0表示有一个待计算的任务;num=0时表示,没有任务;num=-1时表示退出。
以上功能就需要拿到任务的线程将num变为0,表明当前任务已经被抢走。
而main在知道num为0后,下发下一个任务。
当main下发完最后一个任务时,任务被拿走后,main将num变为-1,提醒下游线程退出,上游的线程就等着收尸。是一种能者多劳的一种实现。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#define LEFT 30000000
#define RIGHT 30000200
#define THRNUM 4
static void * thr_prime(void *p);
static int num=0;
static pthread_mutex_t mut_num=PTHREAD_MUTEX_INITIALIZER;
int main()
{
int i,err;
pthread_t tid[THRNUM];
for(i=0;i<=THRNUM;i++)
{
err=pthread_create(tid+i,NULL,thr_prime,(void *)i);
if(err)
{
fprintf(stderr,"pthread_create():%s\n",strerror(err));
exit(1);
}
}
for(i=LEFT;i<=RIGHT;i++)
{
pthread_mutex_lock(&mut_num);
while(num!=0)
{pthread_mutex_unlock(&mut_num);
sched_yield();
pthread_mutex_lock(&mut_num);
}
num=i;
pthread_mutex_unlock(&mut_num);
}
pthread_mutex_lock(&mut_num);
while(num!=0)
{
pthread_mutex_unlock(&mut_num);
sched_yield();
pthread_mutex_lock(&mut_num);
}
num=-1;
pthread_mutex_unlock(&mut_num);
for(i=0;i<=THRNUM;i++)
{
pthread_join(tid[i],NULL);
}
pthread_mutex_destroy(&mut_num);
exit(0);
}
static void * thr_prime(void *p)
{
int i,j,mark;
while(1)
{
pthread_mutex_lock(&mut_num);
while(num==0)
{
pthread_mutex_unlock(&mut_num);
sched_yield();
pthread_mutex_lock(&mut_num);
}
if(num==-1)
{
pthread_mutex_unlock(&mut_num);
break;
}
i=num;
num=0;
pthread_mutex_unlock(&mut_num);
mark=1;
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(NULL);
}
sched_yied():出让调度器给别的进程,不会造成当前进程的调度颠簸,可以看成一种很短的sleep()
运行结果: