现在的位置: 首页 > 综合 > 正文

linux学习之十六—互斥锁

2017年02月05日 ⁄ 综合 ⁄ 共 2736字 ⁄ 字号 评论关闭

线程最大的特点就是资源的共享性,然而资源共享中的同步问题是多线程编程的难点。Linux系统提供了多种方式处理线程间的同步问题,其中最常用的有互斥锁、条件变量和异步信号

互斥锁
互斥锁是通过锁机制来实现线程间的同步。在同一时刻它通常只允许一个线程执行一个关键部分的代码。

1.对互斥锁初始化:

静态赋值法:

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_init函数初始化互斥锁:

int pthread_mutex_init(pthrad_mutex_t *mutex,const pthread_mutexattr_t *mutexattr);

mutexattr为互斥锁的属性。

2.加锁函数:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);

用pthread_mutex_lock()加锁时,如果mutex已经被锁住,当前尝试加锁的线程聚会阻塞,直到互斥锁被其他线程释放。当pthread_mutex_lock函数返回时,说明互斥锁已经被当前线程成功加锁。pthread_mutex_trylock函数则不同,如果mutex已经加锁,它就立即返回,返回的错误码为EBUSY,而不是阻塞等待。

注意:
加锁时,不论那种类型的锁,都不可能被两个不同的线程同时得到,其中一个必须等待解锁。在同一个进程中的线程,如果加锁后没有解锁,则其他线程将无法再获得该锁。

3.解锁函数:

int pthread_mutex_unlock(pthread_mutex_t *mutex);

用pthread_mutex_unlock函数解锁时,要满足两个条件:一是互斥锁必须处于加锁状态,二是调用本函数的线程必须是给互斥锁加锁的线程。解锁后如果其他线程在等待互斥锁,等待队列中第一个线程将获得互斥锁。

示例代码:

#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int myglobal;
void *thread_function(void *arg) 
{
  int i,j;
  for ( i=0; i<20; i++) 
  {
    j=myglobal;
    j=j+1;
    printf(".");
    fflush(stdout);
    sleep(1);
    myglobal=j;
  }
  return NULL;
}
int main(void) {
  pthread_t mythread;
  int i;
  if ( pthread_create( &mythread, NULL, thread_function, NULL) ) 
  {
    printf("error creating thread.");
    abort();
  }
  for ( i=0; i<20; i++) 
  {
    myglobal=myglobal+1;
    printf("o");
    fflush(stdout);
    sleep(1);
  }
  if ( pthread_join ( mythread, NULL ) ) 
  {
    printf("error joining thread.");
    abort();
  }
  printf("\nmyglobal equals %d\n",myglobal);
  exit(0);
}

运行结果:

pc@ubuntu:~/linux_lan/thread/lock$ ./thread1
o.o..o.o.o.o.o.o.o.o.o.o.o.o.o.oo.o.o.o.
myglobal equals 21

分析:

因为主线程和创建的线程都涉及对全局变量myglobal的操作,可以到主线程对全局变量myglobal进行20次加一操作,新线程也对全局变量myglobal进行了20次加一操作,但是返回时,全局变量myglobal为21。

首先在主线程进行了加一操作,sleep(1)时,开始执行新线程,新线程j是在原myglobal基础上加了1,但是没有赋值给myglobal之前,sleep(1),又开始执行主线程,myglobal加1,然后主线程sleep(1),此时myglobal=j,但是此时myglobal已经是加1后的了,所以myglobal值没有变。

现在看看加锁后,程序的运行结果:

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

int myglobal;
pthread_mutex_t mymutex=PTHREAD_MUTEX_INITIALIZER;

void* thread_function(void*arg)
{
   int i,j;
   for(i=0;i<20;i++)
   {
     pthread_mutex_lock(&mymutex);
     j=myglobal;
     j=j+1;
     printf(".");
     fflush(stdout);
     sleep(1);
     myglobal=j;
     pthread_mutex_unlock(&mymutex);
   }
   return NULL;
}   

int main()
{
   pthread_t mythread;
   int i;
   if(pthread_create(&mythread,NULL,thread_function,NULL))
   {
      printf("error creating thread.");
      abort();
   } 
   for(i=0;i<20;i++)
   {
      pthread_mutex_lock(&mymutex);
      myglobal+=1;
      pthread_mutex_unlock(&mymutex);
      printf("o");
      fflush(stdout);
      sleep(1);
   }
   if(pthread_join(mythread,NULL))
   {
      printf("error joining thread.");
      abort(); 
   }
   printf("\nmyglobal equal %d\n",myglobal);
   return 0;
}

运行结果:

pc@ubuntu:~/linux_lan/thread/lock$ ./thread_lock
o....................ooooooooooooooooooo
myglobal equal 40

分析:

可以看到,在加锁的情况下,程序第一次执行新线程时,会一直执行下去,当新线程sleep(1)时,主线程试图加锁,但是因为新线程已经加锁,所以加锁一直失败,主线程处于阻塞状态,直到新线程执行完20此后,才该主线程执行,所以运行结果是40,即主线程与新线程分别对全局变量mygloabal加了20次。

抱歉!评论已关闭.