lock_the_mutex(...)
临界区
unlock_the_mutex(...)
Posix互斥锁被声明为具有pthread_mutex_t 数据类型的变量。如果互斥锁变量是静态分配的,那么我们可以把它初始化成常值PTHREAD_MUTEX_INITIALIZER,例如:
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER
如果互斥锁是动态分配的(例如通过调用malloc),或者分配在共享内存区中,那么我们必须在运行之时通过调用pthread_mutex_init函数来初始化它。
下列三个函数给一个互斥锁上锁和解锁。
#include<pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mptr)
int pthread_mutex_trylock(pthread_mutex_t * nptr)
int pthread_mutex_unlock(pthread_mutex_t * mptr)
均返回:若成功返回0,若出错返回正的Exxx值
如果尝试一个已由另外一个线程锁住的互斥锁上锁,那么pthread_mutex_lock将阻塞到该互斥锁解锁为止。pthread_mutex_trylock是对应的非阻塞函数,如果该互斥锁已锁住,它就返回一个EBUSY错误。
尽管我们说互斥锁保护的是临界区,实际上保护的是在临界区中被操纵的数据。也就是说,互斥锁通常用于保护由多个线程和多个进程分享的共享数据。
(一、)生产者-消费者问题
在单个进程中有多个生产者线程和单个消费者线程。整数数组buff含有被生产和消费的条目(也就是共享数据),在第一个例子中,我们只关心多个生产者线程之间的同步。直到所有生产者线程都生产完成工作后,我们才启动消费者线程。
其代码如下:
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<stdlib.h>
#define MAXNITEMS 1000
#define MAXNTHREADS 100
int nitems;
struct{
pthread_mutex_t mutex;
int buff[MAXNITEMS];
int nput;
int nval;
}shared = {PTHREAD_MUTEX_INITIALIZER};
int min(const int a,const int b)
{
return (a > b ? b : a);
}
void * produce(void *arg)
{
for(;;)
{
pthread_mutex_lock(&shared.mutex);
if(shared.nput >= nitems)
{
pthread_mutex_unlock(&shared.mutex);
return (NULL);
}
shared.buff[shared.nput] = shared.nval;
shared.nput++;
shared.nval++;
pthread_mutex_unlock(&shared.mutex);
*((int *)arg) += 1;
}
}
void * consume(void *arg)
{
int i;
for(i = 0; i < nitems; ++i)
{
#if 0
if(shared.buff[i] != i)
{
printf("buff[%d] = %d\n",i,shared.buff[i]);
}
#endif
printf("buff[%d] = %d\n",i,shared.buff[i]);
}
return (NULL);
}
int main(int argc,char*argv[])
{
int i;
int nthreads, count[MAXNTHREADS];
pthread_t tid_produce[MAXNTHREADS],tid_consume;
if(argc != 3)
{
printf("usage: prodcons<# items> <# threads>\n");
exit(1);
}
nitems = min(atoi(argv[1]),MAXNITEMS);
nthreads = min(atoi(argv[2]),MAXNTHREADS);
pthread_setconcurrency(nthreads);
for(i = 0; i < nthreads; ++i)
{
count[i] = 0;
pthread_create(&tid_produce[i],NULL,produce,&count[i]);
sleep(1);
}
for(i = 0; i < nthreads; ++i)
{
pthread_join(tid_produce[i],NULL);
printf("count[%d] = %d\n",i,count[i]);
}
pthread_create(&tid_consume,NULL,consume,NULL);
pthread_join(tid_consume,NULL);
return 0;
}
其执行结果:
对比上锁与等待:
现在展示互斥锁用于上锁而不用于等待。我们把上一节的生产者-消费者例子改为在所有生产者线程都启动后立即启动消费者线程。这样在生产者线程产生数据的同时,消费者线程就能处理它,而不是像之前那样,消费者线程直到所有生产者线程都完成后才启动。下面给出具体的代码:
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<stdlib.h>
#define MAXNITEMS 1000
#define MAXNTHREADS 100
int nitems;
struct{
pthread_mutex_t mutex;
int buff[MAXNITEMS];
int nput;
int nval;
}shared = {PTHREAD_MUTEX_INITIALIZER};
int min(const int a,const int b)
{
return (a > b ? b : a);
}
void * produce(void *arg)
{
for(;;)
{
pthread_mutex_lock(&shared.mutex);
if(shared.nput >= nitems)
{
pthread_mutex_unlock(&shared.mutex);
return (NULL);
}
shared.buff[shared.nput] = shared.nval;
shared.nput++;
shared.nval++;
pthread_mutex_unlock(&shared.mutex);
*((int *)arg) += 1;
}
}
void consume_wait(int i)
{
for(;;)
{
pthread_mutex_lock(&shared.mutex);
if(i < shared.nput)
{
pthread_mutex_unlock(&shared.mutex);
return ;
}
pthread_mutex_unlock(&shared.mutex);
}
}
void * consume(void *arg)
{
int i;
for(i = 0; i < nitems; ++i)
{
consume_wait(i);
printf("buff[%d] = %d\n",i,shared.buff[i]);
}
return (NULL);
}
int main(int argc,char*argv[])
{
int i;
int nthreads, count[MAXNTHREADS];
pthread_t tid_produce[MAXNTHREADS],tid_consume;
if(argc != 3)
{
printf("usage: prodcons<# items> <# threads>\n");
exit(1);
}
nitems = min(atoi(argv[1]),MAXNITEMS);
nthreads = min(atoi(argv[2]),MAXNTHREADS);
pthread_setconcurrency(nthreads + 1);
for(i = 0; i < nthreads; ++i)
{
count[i] = 0;
pthread_create(&tid_produce[i],NULL,produce,&count[i]);
sleep(1);
}
pthread_create(&tid_consume,NULL,consume,&count[i]);
for(i = 0; i < nthreads; ++i)
{
pthread_join(tid_produce[i],NULL);
printf("count[%d] = %d\n",i,count[i]);
}
pthread_join(tid_consume,NULL);
return 0;
}
上述就是对互斥锁的一些基本的应用。。。