使用读/写锁定

在许多情况下,数据的读取比修改或写入更频繁。

在这些情况下,可允许线程在保留锁的同时进行读取,且在修改数据时只允许一个线程保留锁。 多阅读器单写程序锁定 (或读/写锁定) 执行此操作。 为读取或写入获取读/写锁定,然后释放。 获取读写锁的线程一定是释放它的线程。

读/写属性对象

pthread_rwlockattr_init 子例程初始化读写锁属性对象 (attr)。 所有属性的缺省值由该实现来定义。 如果 pthread_rwlockattr_init 子例程指定已初始化的读/写锁定属性对象,那么会发生意外结果。

以下示例说明了如何以 attr 对象调用 pthread_rwlockattr_init 子例程:
pthread_rwlockattr_t    attr;
和:
pthread_rwlockattr_init(&attr);

在使用读/写锁定属性对象来初始化一个或多个读/写锁定之后,任何影响属性对象 (包括破坏) 的功能都不会影响任何先前初始化的读/写锁定。

pthread_rwlockattr_destroy 子例程破坏读/写锁定属性对象。 如果该对象在其被别的 pthread_rwlockattr_init 子例程的调用重新初始化前被使用,那么可能发生无法预料的结果。 实现可能导致 pthread_rwlockattr_destroy 子例程将由 attr 对象所引用的对象设为无效值。

创建和销毁读/写锁定

pthread_rwlock_init 子例程使用 attr 对象引用的属性来初始化 rwlock 对象引用的读/写锁定。 如果 attr 对象为 NULL ,那么将使用缺省读/写锁定属性; 其效果与传递缺省读写锁定属性对象的地址相同。 成功初始化后,读/写锁定的状态将初始化并解锁。 初始化后,该锁可以被使用任意次,而无需重新初始化。 如果调用 pthread_rwlock_init 子例程时指定了已初始化的读/写锁定,或者未首先初始化就使用了读/写锁定,那么会发生意外结果。

如果 pthread_rwlock_init 子例程失败,那么 rwlock 对象不会被初始化且其内容是不确定的。

pthread_rwlock_destroy 子例程破坏 rwlock 对象所引用的读/写锁定对象,并释放该锁定所使用的任何资源。 在以下任一情况中都可能发生无法预料的结果:
  • 如果该锁在其被另一个对 pthread_rwlock_init 子例程的调用重新初始化前被使用。
  • 实现可能导致 pthread_rwlock_destroy 子例程将由 rwlock 对象所引用的对象设为无效值。 如果任何线程保留 rwlock 对象时 pthread_rwlock_destroy 被调用,那么可能发生无法预料的结果。
  • 尝试销毁未初始化的读/写锁定会导致意外结果。 可以使用 pthread_rwlock_init 子例程重新初始化损坏的读/写锁定对象。 如果在读/写锁定对象被破坏后被引用,那么可能会发生意外结果。
在缺省读/写锁定属性适当的情况下,使用 PTHREAD_RWLOCK_INITIALIZER 宏来初始化静态分配的读/写锁定。 例如:
pthread_rwlock_t        rwlock1 = PTHREAD_RWLOCK_INITIALIZER;
通过使用带有指定为 NULL 的 attr 参数的 pthread_rwlock_init 子例程的调用,效果类似于动态初始化,只不过没有执行错误检查。 例如:
pthread_rwlock_init(&rwlock2, NULL);
以下示例说明如何在初始化 attr 参数的情况下使用 pthread_rwlock_init 子例程。 有关如何初始化 attr 参数的示例,请参阅 读/写属性对象
pthread_rwlock_init(&rwlock, &attr);

锁定用于读取的读/写锁定对象

pthread_rwlock_rdlock 子例程将读锁定应用于 rwlock 对象引用的读/写锁定。 如果写程序未持有该锁,并且没有写程序在该锁上被阻塞,那么调用线程将获取读锁。 未指定在写程序没有保留该锁且有写程序在等待该锁时调用线程是否要获取该锁。 如果写程序保留了该锁,调用线程将不会获取该读取锁。 如果没有获取读取锁,调用线程就不从 pthread_rwlock_rdlock 调用返回,直到它获取该锁。 如果调用线程在进行调用时保留了 rwlock 对象的写入锁,那么结果是不确定的。

一个线程可以在 rwlock 对象上挂起多个并发读锁定 (即,成功调用 pthread_rwlock_rdlock 子例程 n 次)。 如果是这样的话,该线程必须执行匹配的解锁(即它必须调用 pthread_rwlock_unlock 子例程 n 次)。

pthread_rwlock_tryrdlock 子例程应用类似于 pthread_rwlock_rdlock 子例程的读锁,例外情况是,如果任何线程在 鲁洛克 对象上持有写锁,或者如果写程序在 鲁洛克 对象上受阻,那么子例程将失败。 如果使用未初始化的读/写锁定来调用任何这些函数,那么将未定义结果。

如果将信号传递给等待读/写锁定的线程,那么当从信号处理程序返回时,该线程将恢复等待读/写锁定以进行读取,就好像它未中断一样。

锁定用于写入的读/写锁定对象

pthread_rwlock_wrlock 子例程将写锁定应用于 rwlock 对象引用的读/写锁定。 如果没有其他线程 (阅读器或写程序) 持有 rwlock 对象上的读/写锁定,那么调用线程将获取写锁定。 否则,该线程不会从 pthread_rwlock_wrlock 调用返回,直到它可以获取该锁。 如果调用线程在执行调用时持有读/写锁定 (无论是读或写锁定) ,那么结果未定义。

pthread_rwlock_trywrlock 子例程对写入锁的应用与 pthread_rwlock_wrlock 子例程大致相似,除了一种情况,即如果任何线程当前保留了 rwlock 以进行读取或写入时该函数会失败。 如果使用未初始化的读/写锁定来调用任何这些函数,那么将未定义结果。

如果将信号传递到等待读/写锁定以进行写操作的线程,那么当从信号处理程序返回时,该线程将恢复等待读/写锁定以进行写操作,就好像它未中断一样。

首选写程序线程而不是读程序线程

应用程序可以使用 pthread_rwlock_attr_setfavorwriters_np 子例程来初始化读/写锁定的属性。 您可以指定 pthread 库,以确定在写方式下需要读/写锁定的线程的调度优先级。 当 pthread 库调度写程序线程 (写数据的线程) 以在写方式下获取读/写锁时, pthread 库不支持由在读方式下持有读/写锁的线程进行递归。 当线程多次在读方式下持有读/写锁定时,可能会发生意外的结果。

pthread_rwlock_attr_getfavorwriters_np 子例程返回在读/写锁定属性结构中设置的当前首选项。 缺省情况下,阅读器线程优先于写程序线程以获取读/写锁定。

样本读/写锁定程序

以下样本程序演示了如何使用锁定子例程。 要运行这些程序,您需要 check.h 文件和 makefile

check.h 文件:
#include stdio.h
#include stdio.h
#include stdio.h
#include stdio.h

/* Simple function to check the return code and exit the program
   if the function call failed
   */
static void compResults(char *string, int rc) {
  if (rc) {
    printf("Error on : %s, rc=%d",
           string, rc);
    exit(EXIT_FAILURE);
  }
  return;
}
Make 文件:
CC_R = xlc_r

TARGETS = test01 test02 test03

OBJS = test01.o test02.o test03.o

SRCS = $(OBJS:.o=.c)

$(TARGETS): $(OBJS)
    $(CC_R) -o $@ $@.o

clean:
    rm $(OBJS) $(TARGETS)

run:
    test01
    test02
    test03

单线程示例

以下示例以单线程来使用 pthread_rwlock_tryrdlock 子例程。 有关将 pthread_rwlock_tryrdlock 子例程用于多个线程的示例,请参阅 多线程示例

Example: test01.c

#define _MULTI_THREADED
#include pthread.h
#include stdio.h
#include "check.h"

pthread_rwlock_t       rwlock = PTHREAD_RWLOCK_INITIALIZER;

void *rdlockThread(void *arg)
{
  int             rc;
  int             count=0;

  printf("Entered thread, getting read lock with mp wait\n");
  Retry:
  rc = pthread_rwlock_tryrdlock(&rwlock);
  if (rc == EBUSY) {
    if (count >= 10) {
      printf("Retried too many times, failure!\n");

      exit(EXIT_FAILURE);
    }
    ++count;
    printf("Could not get lock, do other work, then RETRY...\n");
    sleep(1);
    goto Retry;
  }
  compResults("pthread_rwlock_tryrdlock() 1\n", rc);

  sleep(2);

  printf("unlock the read lock\n");
  rc = pthread_rwlock_unlock(&rwlock);
  compResults("pthread_rwlock_unlock()\n", rc);

  printf("Secondary thread complete\n");
  return NULL;
}

int main(int argc, char **argv)
{
  int                   rc=0;
  pthread_t             thread;

  printf("Enter test case - %s\n", argv[0]);

  printf("Main, get the write lock\n");
  rc = pthread_rwlock_wrlock(&rwlock);
  compResults("pthread_rwlock_wrlock()\n", rc);

  printf("Main, create the try read lock thread\n");
  rc = pthread_create(&thread, NULL, rdlockThread, NULL);
  compResults("pthread_create\n", rc);

  printf("Main, wait a bit holding the write lock\n");
  sleep(5);

  printf("Main, Now unlock the write lock\n");
  rc = pthread_rwlock_unlock(&rwlock);
  compResults("pthread_rwlock_unlock()\n", rc);

  printf("Main, wait for the thread to end\n");
  rc = pthread_join(thread, NULL);
  compResults("pthread_join\n", rc);

  rc = pthread_rwlock_destroy(&rwlock);
  compResults("pthread_rwlock_destroy()\n", rc);
  printf("Main completed\n");
  return 0;
}

此样本程序的输出与以下内容相似:

Enter test case - ./test01
Main, get the write lock
Main, create the try read lock thread
Main, wait a bit holding the write lock

Entered thread, getting read lock with mp wait
Could not get lock, do other work, then RETRY...
Could not get lock, do other work, then RETRY...
Could not get lock, do other work, then RETRY...
Could not get lock, do other work, then RETRY...
Could not get lock, do other work, then RETRY...
Main, Now unlock the write lock
Main, wait for the thread to end
unlock the read lock
Secondary thread complete
Main completed

多线程示例

以下示例以多线程来使用 pthread_rwlock_tryrdlock 子例程。 有关将 pthread_rwlock_tryrdlock 子例程与单个线程配合使用的示例,请参阅 单线程示例

Example: test02.c

#define _MULTI_THREADED
#include pthread.h
#include stdio.h
#include "check.h"

pthread_rwlock_t       rwlock = PTHREAD_RWLOCK_INITIALIZER;

void *wrlockThread(void *arg)
{
  int             rc;
  int             count=0;

  printf("%.8x: Entered thread, getting write lock\n",
         pthread_self());
  Retry:
  rc = pthread_rwlock_trywrlock(&rwlock);
  if (rc == EBUSY) {
    if (count >= 10) {
      printf("%.8x: Retried too many times, failure!\n",
             pthread_self());
      exit(EXIT_FAILURE);
    }

    ++count;
    printf("%.8x: Go off an do other work, then RETRY...\n",
           pthread_self());
    sleep(1);
    goto Retry;
  }
  compResults("pthread_rwlock_trywrlock() 1\n", rc);
  printf("%.8x: Got the write lock\n", pthread_self());

  sleep(2);

  printf("%.8x: Unlock the write lock\n",
         pthread_self());
  rc = pthread_rwlock_unlock(&rwlock);
  compResults("pthread_rwlock_unlock()\n", rc);

  printf("%.8x: Secondary thread complete\n",
         pthread_self());
  return NULL;
}

int main(int argc, char **argv)
{
  int                   rc=0;
  pthread_t             thread, thread2;

  printf("Enter test case - %s\n", argv[0]);

  printf("Main, get the write lock\n");
  rc = pthread_rwlock_wrlock(&rwlock);
  compResults("pthread_rwlock_wrlock()\n", rc);

  printf("Main, create the timed write lock threads\n");
  rc = pthread_create(&thread, NULL, wrlockThread, NULL);
  compResults("pthread_create\n", rc);

  rc = pthread_create(&thread2, NULL, wrlockThread, NULL);
  compResults("pthread_create\n", rc);

  printf("Main, wait a bit holding this write lock\n");
  sleep(1);

  printf("Main, Now unlock the write lock\n");
  rc = pthread_rwlock_unlock(&rwlock);
  compResults("pthread_rwlock_unlock()\n", rc);

  printf("Main, wait for the threads to end\n");
  rc = pthread_join(thread, NULL);
  compResults("pthread_join\n", rc);

  rc = pthread_join(thread2, NULL);
  compResults("pthread_join\n", rc);

  rc = pthread_rwlock_destroy(&rwlock);
  compResults("pthread_rwlock_destroy()\n", rc);
  printf("Main completed\n");
  return 0;
}

此样本程序的输出与以下内容相似:

Enter test case - ./test02
Main, get the write lock
Main, create the timed write lock threads
Main, wait a bit holding this write lock
00000102: Entered thread, getting write lock 
00000102: Go off an do other work, then RETRY...
00000203: Entered thread, getting write lock
00000203: Go off an do other work, then RETRY...
Main, Now unlock the write lock
Main, wait for the threads to end
00000102: Got the write lock
00000203: Go off an do other work, then RETRY...
00000203: Go off an do other work, then RETRY...
00000102: Unlock the write lock
00000102: Secondary thread complete
00000203: Got the write lock
00000203: Unlock the write lock
00000203: Secondary thread complete
Main completed

读/写读/锁定示例

以下示例使用 pthread_rwlock_rdlock 子例程来实现读/写读锁定:

Example: test03.c

#define _MULTI_THREADED
#include pthread.h
#include stdio.h
#include "check.h"

pthread_rwlock_t       rwlock;

void *rdlockThread(void *arg)
{
  int rc;

  printf("Entered thread, getting read lock\n");
  rc = pthread_rwlock_rdlock(&rwlock);
  compResults("pthread_rwlock_rdlock()\n", rc);
  printf("got the rwlock read lock\n");

  sleep(5);

  printf("unlock the read lock\n");
  rc = pthread_rwlock_unlock(&rwlock);
  compResults("pthread_rwlock_unlock()\n", rc);
  printf("Secondary thread unlocked\n");
  return NULL;
}

void *wrlockThread(void *arg)
{
  int rc;

  printf("Entered thread, getting write lock\n");
  rc = pthread_rwlock_wrlock(&rwlock);
  compResults("pthread_rwlock_wrlock()\n", rc);

  printf("Got the rwlock write lock, now unlock\n");
  rc = pthread_rwlock_unlock(&rwlock);
  compResults("pthread_rwlock_unlock()\n", rc);
  printf("Secondary thread unlocked\n");
  return NULL;
}



int main(int argc, char **argv)
{
  int                   rc=0;
  pthread_t             thread, thread1;

  printf("Enter test case - %s\n", argv[0]);

  printf("Main, initialize the read write lock\n");
  rc = pthread_rwlock_init(&rwlock, NULL);
  compResults("pthread_rwlock_init()\n", rc);

  printf("Main, grab a read lock\n");
  rc = pthread_rwlock_rdlock(&rwlock);
  compResults("pthread_rwlock_rdlock()\n",rc);

  printf("Main, grab the same read lock again\n");
  rc = pthread_rwlock_rdlock(&rwlock);
  compResults("pthread_rwlock_rdlock() second\n", rc);

  printf("Main, create the read lock thread\n");
  rc = pthread_create(&thread, NULL, rdlockThread, NULL);
  compResults("pthread_create\n", rc);

  printf("Main - unlock the first read lock\n");
  rc = pthread_rwlock_unlock(&rwlock);
  compResults("pthread_rwlock_unlock()\n", rc);

  printf("Main, create the write lock thread\n");
  rc = pthread_create(&thread1, NULL, wrlockThread, NULL);
  compResults("pthread_create\n", rc);

  sleep(5);
  printf("Main - unlock the second read lock\n");
  rc = pthread_rwlock_unlock(&rwlock);
  compResults("pthread_rwlock_unlock()\n", rc);

  printf("Main, wait for the threads\n");
  rc = pthread_join(thread, NULL);
  compResults("pthread_join\n", rc);

  rc = pthread_join(thread1, NULL);
  compResults("pthread_join\n", rc);

  rc = pthread_rwlock_destroy(&rwlock);
  compResults("pthread_rwlock_destroy()\n", rc);

  printf("Main completed\n");
  return 0;
}

此样本程序的输出与以下内容相似:

$ ./test03
Enter test case - ./test03
Main, initialize the read write lock
Main, grab a read lock
Main, grab the same read lock again
Main, create the read lock thread
Main - unlock the first read lock
Main, create the write lock thread
Entered thread, getting read lock
got the rwlock read lock
Entered thread, getting write lock
Main - unlock the second read lock
Main, wait for the threads
unlock the read lock
Secondary thread unlocked
Got the rwlock write lock, now unlock
Secondary thread unlocked
Main completed