Chinaunix首页 | 论坛 | 博客
  • 博客访问: 261925
  • 博文数量: 94
  • 博客积分: 526
  • 博客等级: 中士
  • 技术积分: 687
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-09 10:02
文章存档

2014年(1)

2013年(10)

2012年(83)

分类: C/C++

2012-09-08 11:31:11

互斥锁pthread_mutex_t的使用

有两种方法创建互斥锁,静态方式和动态方式。POSIX定义了一个宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁,方法如下:

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;

 在LinuxThreads实现中,pthread_mutex_t是一个结构,而PTHREAD_MUTEX_INITIALIZER则是一个结构常量。

  动态方式是采用pthread_mutex_init()函数来初始化互斥锁,API定义如下:

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)

其中mutexattr用于指定互斥锁属性(见下),如果为NULL则使用缺省属性。

  pthread_mutex_destroy ()用于注销一个互斥锁,API定义如下:

int pthread_mutex_destroy(pthread_mutex_t *mutex)

销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。由于在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的 pthread_mutex_destroy()除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。

  2. 互斥锁属性

  互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。当前(glibc2.2.3,linuxthreads0.9)有四个值可供选择:

  * PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。

  * PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。

  * PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。

  * PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。

  3. 锁操作

  锁操作主要包括加锁pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁 pthread_mutex_trylock()三个,不论哪种类型的锁,都不可能被两个不同的线程同时得到,而必须等待解锁。对于普通锁和适应锁类型,解锁者可以是同进程内任何线程;而检错锁则必须由加锁者解锁才有效,否则返回EPERM;对于嵌套锁,文档和实现要求必须由加锁者解锁,但实验结果表明并没有这种限制,这个不同目前还没有得到解释。在同一进程中的线程,如果加锁后没有解锁,则任何其他线程都无法再获得锁。

  int pthread_mutex_lock(pthread_mutex_t *mutex)

  int pthread_mutex_unlock(pthread_mutex_t *mutex)

  int pthread_mutex_trylock(pthread_mutex_t *mutex)

  pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待。

/*==============================================================================
bufsem.c
功能:主进程负责向队列(就2个元素)写入数据,创建的两个线程负责读取数据。
同步互斥:主进程和线程之间用信号量sem_t进行同步,线程之间依靠互斥锁进行互斥操作。
问题:进程和线程都使用了信号量,可不可以免去互斥锁?

注意:直接编译gcc -o bufsem bufsem.c会出现如下错误

undefined reference to `sem_init'
undefined reference to `sem_post'
undefined reference to `sem_wait'

编译选项需要加入一个多线程

gcc -pthread -o bufsem bufsem.c

*/==============================================================================

#include
#include
#include
#include
#include
#include

#define N 2 //队列中元素的数量

struct _buf_
{
char *buf[N];
int front, rear; //有from和rear,显然是个队列
}BUF;

void * thread_function_r(void *arg); //线程函数
/*
信号量的数据类型为结构sem_t,它本质上是一个长整型的数。函数sem_init()用来初始化一个信号量。它的原型为:  
extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));  
__sem为指向信号量结构的一个指针;__pshared不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享;__value给出了信号量的初始值。  
*/
sem_t sem_r, sem_w; //semphore,信号量,本程序用于保证进程和线程间的同步

pthread_mutex_t mutex; //互斥锁,保证线程访问的互斥,步骤一,定义数据类型

int main(int argc, char *argv[])
{

int res;
pthread_t a_thread_r, b_thread_r; //线程ID变量,与进程类似,进程为pid_t

for (res=0; resBUF.rear = BUF.front = 0;
/*信号量用sem_init函数创建的,下面是它的说明:
  #include
        int sem_init (sem_t *sem, int pshared, unsigned int value);
    这个函数的作用是对由sem指定的信号量进行初始化,设置好它的共享选项,并指定一个整数类型的初始值。pshared参数控制着信号量的类型。如果 pshared的值是0,就表示它是当前里程的局部信号量;否则,其它进程就能够共享这个信号量。
*/
res = sem_init(&sem_r, 0, 0);
if ( res != 0) 
{
   perror(strerror(errno));
   exit(EXIT_FAILURE);
}
res = sem_init(&sem_w, 0, 2);
if ( res != 0) 
{
   perror(strerror(errno));
   exit(EXIT_FAILURE);
}

if ( pthread_mutex_init(&mutex, NULL) != 0 ) //互斥锁,保证线程访问的互斥,步骤二,初始化,默认属性初始化互斥量——NULL参数的功能
{
   perror(strerror(errno));
   exit(EXIT_FAILURE);
}
/*
#include
  int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict attr,void*(*start_rtn)(void*),void *restrict arg);
  返回值:若成功则返回0,否则返回出错编号
  返回成功时,由tidp指向的内存单元被设置为新创建线程的线程ID。attr参数用于制定各种不同的线程属性。新创建的线程从start_rtn函数的地址开始运行,该函数只有一个无类型指针参数arg,如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg的参数传入。
*/
res = pthread_create(&a_thread_r, NULL, thread_function_r, NULL); //创建读取线程一
if ( res != 0 )
{
   perror(strerror(errno));
   exit(EXIT_FAILURE);
}
res = pthread_create(&b_thread_r, NULL, thread_function_r, NULL); //创建读取线程二
if ( res != 0 )
{
   perror(strerror(errno));
   exit(EXIT_FAILURE);
}

while(1) //【功能】主进程执行写入操作
{
    sem_wait(&sem_w); //函数sem_wait( sem_t *sem )被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少。函数sem_trywait ( sem_t *sem )是函数sem_wait()的非阻塞版本,它直接将信号量sem的值减一。函数sem_destroy(sem_t *sem)用来释放信号量sem。
    fputs("Input any words, its words must less than 30: ", stdout);
    fgets(BUF.buf[BUF.rear], 30, stdin);
    if (strcmp("end\n", BUF.buf[(BUF.front) % N]) == 0 ) break;
    BUF.rear = (BUF.rear + 1) % N;
    sem_post(&sem_r); //函数sem_post( sem_t *sem )用来增加信号量的值。当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不再阻塞,选择机制同样是由线程的调度策略决定的。
    usleep(1);
}

return 0;
}

void * thread_function_r(void *arg) //【功能】线程负责读取
{
while(1)
{
   sem_wait(&sem_r);
   pthread_mutex_lock(&mutex); //获取互斥量(互斥锁),另外有pthread_mutex_trylock尝试对互斥量加锁,如果失败返回EBUSY
   fprintf(stdout, "Print:BUF.buf[%d] by %u: %s", BUF.front,pthread_self(), BUF.buf[BUF.front]);
   BUF.front = (BUF.front + 1) % N;
   pthread_mutex_unlock(&mutex); //释放互斥锁
   sem_post(&sem_w);
}
}

阅读(2633) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~