读写锁分析

   最近要做一个FS多线程优化的项目:初步想法是将原来的静态锁去掉,换成并发性能更好的读写锁。我这里只是做个知识储备。

我们先看下源码:

int
__pthread_rwlock_init (rwlock, attr)
     pthread_rwlock_t *rwlock;
     const pthread_rwlockattr_t *attr;
{
  const struct pthread_rwlockattr *iattr;

  iattr = ((const struct pthread_rwlockattr *) attr) ?: &default_attr;

  rwlock->__data.__lock = 0;
  rwlock->__data.__flags
    = iattr->lockkind == PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP;
  rwlock->__data.__nr_readers = 0;
  rwlock->__data.__writer = 0;
  rwlock->__data.__readers_wakeup = 0;
  rwlock->__data.__writer_wakeup = 0;
  rwlock->__data.__nr_readers_queued = 0;
  rwlock->__data.__nr_writers_queued = 0;

  return 0;
}
 
   
 
   
 
   
 
   
 
  
 

pthread_rwlock_init初始化锁,这里值得一提的是读写锁的默认属性PTHREAD_RWLOCK_DEFAULT_NP,这个值之间决定__flags字段。

如果你想要用到写优先的特性,所以你必须用到PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP,而非一些博文上所说的

其他值。

/* Acquire read lock for RWLOCK.  */
int
__pthread_rwlock_rdlock (rwlock)
     pthread_rwlock_t *rwlock;
{
  int result = 0;

  /* Make sure we are along.  */
  lll_mutex_lock (rwlock->__data.__lock);

  while (1)
    {
      /* Get the rwlock if there is no writer...  */
      if (rwlock->__data.__writer == 0
	  /* ...and if either no writer is waiting or we prefer readers.  */
	  && (!rwlock->__data.__nr_writers_queued
	      || rwlock->__data.__flags == 0))
	{
	  /* Increment the reader counter.  Avoid overflow.  */
	  if (__builtin_expect (++rwlock->__data.__nr_readers == 0, 0))
	    {
	      /* Overflow on number of readers.	 */
	      --rwlock->__data.__nr_readers;
	      result = EAGAIN;
	    }

	  break;
	}

      /* Make sure we are not holding the rwlock as a writer.  This is
	 a deadlock situation we recognize and report.  */
      if (__builtin_expect (rwlock->__data.__writer
			    == THREAD_GETMEM (THREAD_SELF, tid), 0))
	{
	  result = EDEADLK;
	  break;
	}

      /* Remember that we are a reader.  */
      if (__builtin_expect (++rwlock->__data.__nr_readers_queued == 0, 0))
	{
	  /* Overflow on number of queued readers.  */
	  --rwlock->__data.__nr_readers_queued;
	  result = EAGAIN;
	  break;
	}

      int waitval = rwlock->__data.__readers_wakeup;

      /* Free the lock.  */
      lll_mutex_unlock (rwlock->__data.__lock);

      /* Wait for the writer to finish.  */
      lll_futex_wait (&rwlock->__data.__readers_wakeup, waitval);

      /* Get the lock.  */
      lll_mutex_lock (rwlock->__data.__lock);

      --rwlock->__data.__nr_readers_queued;
    }

  /* We are done, free the lock.  */
  lll_mutex_unlock (rwlock->__data.__lock);

  return result;
}


我们看到这个判断

/* Get the rwlock if there is no writer...  */
      if (rwlock->__data.__writer == 0
	  /* ...and if either no writer is waiting or we prefer readers.  */
	  && (!rwlock->__data.__nr_writers_queued
	      || rwlock->__data.__flags == 0))


__flags在这里起到了作用。下面是pthread_rwlock_wrlock,pthread_rwlock_unlock源码

/* Acquire write lock for RWLOCK.  */
int
__pthread_rwlock_wrlock (rwlock)
     pthread_rwlock_t *rwlock;
{
  int result = 0;

  /* Make sure we are along.  */
  lll_mutex_lock (rwlock->__data.__lock);

  while (1)
    {
      /* Get the rwlock if there is no writer and no reader.  */
      if (rwlock->__data.__writer == 0 && rwlock->__data.__nr_readers == 0)
	{
	  /* Mark self as writer.  */
	  rwlock->__data.__writer = THREAD_GETMEM (THREAD_SELF, tid);
	  break;
	}

      /* Make sure we are not holding the rwlock as a writer.  This is
	 a deadlock situation we recognize and report.  */
      if (__builtin_expect (rwlock->__data.__writer
			    == THREAD_GETMEM (THREAD_SELF, tid), 0))
	{
	  result = EDEADLK;
	  break;
	}

      /* Remember that we are a writer.  */
      if (++rwlock->__data.__nr_writers_queued == 0)
	{
	  /* Overflow on number of queued writers.  */
	  --rwlock->__data.__nr_writers_queued;
	  result = EAGAIN;
	  break;
	}

      int waitval = rwlock->__data.__writer_wakeup;

      /* Free the lock.  */
      lll_mutex_unlock (rwlock->__data.__lock);

      /* Wait for the writer or reader(s) to finish.  */
      lll_futex_wait (&rwlock->__data.__writer_wakeup, waitval);

      /* Get the lock.  */
      lll_mutex_lock (rwlock->__data.__lock);

      /* To start over again, remove the thread from the writer list.  */
      --rwlock->__data.__nr_writers_queued;
    }

  /* We are done, free the lock.  */
  lll_mutex_unlock (rwlock->__data.__lock);

  return result;
}
/* Unlock RWLOCK.  */
int
__pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
{
  lll_mutex_lock (rwlock->__data.__lock);
  if (rwlock->__data.__writer)
    rwlock->__data.__writer = 0;
  else
    --rwlock->__data.__nr_readers;
  if (rwlock->__data.__nr_readers == 0)
    {
      if (rwlock->__data.__nr_writers_queued)
	{
	  ++rwlock->__data.__writer_wakeup;
	  lll_mutex_unlock (rwlock->__data.__lock);
	  lll_futex_wake (&rwlock->__data.__writer_wakeup, 1);
	  return 0;
	}
      else if (rwlock->__data.__nr_readers_queued)
	{
	  ++rwlock->__data.__readers_wakeup;
	  lll_mutex_unlock (rwlock->__data.__lock);
	  lll_futex_wake (&rwlock->__data.__readers_wakeup, INT_MAX);
	  return 0;
	}
    }
  lll_mutex_unlock (rwlock->__data.__lock);
  return 0;
}

从源码看出:1.读写锁无法递归,否则会EDEADLK;2.如果你想用到写优先,你必须设置属性PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP。

 

下篇博文:使用Doxygen生成函数调用图。

你可能感兴趣的:(操作系统,C/C++)