代码改变世界

C# 线程手册 第三章 使用线程 ReaderWriterLock 类

2012-02-07 21:53  DanielWise  阅读(6001)  评论(1编辑  收藏  举报

一个ReaderWriterLock 类定义一个实现单写多读语义的锁。这个类通常用在能被多个线程读取但是仅能被一个线程写入的文件操作时使用。下面是ReaderWriterLock类中的四个主要方法:

  a. AcquireReaderLock(): 这个重载方法获取一个读者锁,接受一个整型或者TimeSpan类型的timeout 值。timeout是一个检测死锁的利器。

  b. AcquireWriterLock():  这个重载方法获取一个写者锁,接受一个整型或者TimeSpan类型的timeout 值。

  c. ReleaseReaderLock(): 释放读者锁。

  d. ReleaseWriterLock(): 释放写者锁。

 

使用ReaderWriterLock类可以让多线程安全地进行数据并发读取。只有当线程正在更新的数据锁定。读者线程可以再没有写者拥有锁的时候获得锁。写者线程可以再没有读者线程或者写者线程拥有锁的时候获得锁。

下面的列表ReadeWriteLock.cs, 描述了如何使用ReaderWriterLock()锁:

/*************************************
/* copyright (c) 2012 daniel dong
 * 
 * author:daniel dong
 * blog:  www.cnblogs.com/danielwise
 * email: guofoo@163.com
 * 
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace ReadWriteLock
{
    public class ReadWrite
    {
        private ReaderWriterLock rwl;
        private int x;
        private int y;

        public ReadWrite()
        {
            rwl = new ReaderWriterLock();
        }

        public void ReadInts(ref int a, ref int b)
        {
            rwl.AcquireReaderLock(Timeout.Infinite);
            try
            {
                a = this.x;
                b = this.y;
            }
            finally
            {
                rwl.ReleaseReaderLock();
            }
        }

        public void WriteInts(int a, int b)
        {
            rwl.AcquireWriterLock(Timeout.Infinite);
            try
            {
                this.x = a;
                this.y = b;
                Console.WriteLine("x = " + this.x
                    + " y = " + this.y
                    + " ThreadID = " + Thread.CurrentThread.GetHashCode());
            }
            finally
            {
                rwl.ReleaseWriterLock();
            }
        }
    }

    public class RWApp
    {
        private ReadWrite rw = new ReadWrite();

        public static void Main(string[] args)
        {
            RWApp e = new RWApp();

            //Writer Threads
            Thread wt1 = new Thread(new ThreadStart(e.Write));
            wt1.Start();
            Thread wt2 = new Thread(new ThreadStart(e.Write));
            wt2.Start();

            //Reader Threads
            Thread rt1 = new Thread(new ThreadStart(e.Read));
            rt1.Start();
            Thread rt2 = new Thread(new ThreadStart(e.Read));
            rt2.Start();

            Console.ReadLine();
        }

        private void Write()
        {
            int a = 10;
            int b = 11;
            Console.WriteLine("************** Write *************");

            for (int i = 0; i < 5; i++)
            {
                this.rw.WriteInts(a++, b++);
                Thread.Sleep(1000);
            }
        }

        private void Read()
        {
            int a = 10;
            int b = 11;
            Console.WriteLine("************** Read *************");

            for (int i = 0; i < 5; i++)
            {
                this.rw.ReadInts(ref a, ref b);
                Console.WriteLine("For i = " + i
                    + " a = " + a
                    + " b = " + b
                    + " TheadID = " + Thread.CurrentThread.GetHashCode());
                Thread.Sleep(1000);
            }
        }
    }
}

ReadWriteLock 的输出结果可能与下表类似:

ReaderWriterLock

在上面的列表中,线程wt1 和 wt2 是WriteInts()方法中获得写锁的写者线程,线程rt1 和 rt2 是在ReadInts()方法中获得读者锁的读者线程。在WriteInts()方法中,变量x 和 y 的值分别被改成a 和 b. 当线程wt1 或 wt2 通过调用AcquireWriterLock() 方法获得一个写者锁后,那么直到这个线程通过调用ReleaseWriterLock()方法释放锁之前任何其他线程(包括读者线程rt1 和 rt2)都不被允许访问相应对象。这个行为与Monitors类似 。在ReadInts()方法中,线程rt1 和 rt2 通过调用AcquireReaderLock()方法获得读者锁, 这两个线程可以并发地访问变量x 和 y. 直到读者线程释放它们的读者锁以后,写者线程(wt1 和 wt2)才被允许访问对应对象。只有读者线程在获得读者锁以后才可以并发地访问。

Monitors类对于只想来读数据而非写数据来说过于“安全”了。Monitors 也有一些性能问题,对于只读类型的访问来说,性能瓶颈是可以避免的。ReaderWriterLock类通过允许任意数量的线程并发地读取数据来提供一个解决数据读-写问题的完美方案。当线程更新数据时锁住数据。当没有写者线程拥有锁的时候写者线程可以获得锁。写者锁可以在没有读者线程或者写者线程拥有锁的时候获得锁。因此,ReaderWriterLock 就像是一段关键部分代码, 它也支持一个timeout 值,而这方面在检测死锁时非常有用。

 

下一篇介绍手动同步…