내보내기(0) 인쇄
모두 확장

ReaderWriterLock 클래스

단일 작성기 및 다중 판독기를 지원하는 잠금을 정의합니다.

네임스페이스: System.Threading
어셈블리: mscorlib(mscorlib.dll)

[ComVisibleAttribute(true)] 
public sealed class ReaderWriterLock : CriticalFinalizerObject
/** @attribute ComVisibleAttribute(true) */ 
public final class ReaderWriterLock extends CriticalFinalizerObject
ComVisibleAttribute(true) 
public final class ReaderWriterLock extends CriticalFinalizerObject

Note참고

이 클래스에 적용되는 HostProtectionAttribute 특성의 Resources 속성 값은 Synchronization | ExternalThreading입니다. HostProtectionAttribute는 대개 아이콘을 두 번 클릭하거나, 명령을 입력하거나, 브라우저에서 URL을 입력하여 시작되는 데스크톱 응용 프로그램에 영향을 미치지 않습니다. 자세한 내용은 HostProtectionAttribute 클래스나 SQL Server 프로그래밍 및 호스트 보호 특성을 참조하십시오.

ReaderWriterLock을 사용하여 리소스에 대한 액세스를 동기화합니다. 지정된 시점에 여러 스레드에 대한 동시 읽기 액세스 또는 단일 스레드에 대한 쓰기 액세스를 허용합니다. 리소스가 자주 변경되는 경우 ReaderWriterLock을 사용하면 Monitor 같이 한 번에 하나씩 수행되는 단순 잠금을 사용할 때보다 효과적으로 처리할 수 있습니다.

ReaderWriterLock은 주로 읽기 작업을 위해 액세스가 수행되며 짧은 기간에 걸쳐 가끔 쓰기가 수행되는 경우에 가장 잘 작동합니다. 다중 판독기와 단일 작성기는 서로 번갈아 작동되므로 판독기와 작성기 모두 오래 동안 잠금 상태가 유지되지 않습니다.

Note참고

판독기나 작성기의 잠금 상태를 오래 동안 유지하면 다른 스레드에서 실행할 수 없게 됩니다. 최상의 성능을 위해서는 쓰기 기간이 최소화되도록 응용 프로그램을 재구성하는 것이 좋습니다.

스레드에서 판독기 또는 작성기에 대해 잠금 상태를 유지할 수는 있지만 동시에 두 가지 잠금 상태를 모두 유지할 수는 없습니다. 작성기 잠금을 가져오기 위해 판독기 잠금을 해제하는 대신 UpgradeToWriterLockDowngradeFromWriterLock을 사용할 수 있습니다.

재귀적 잠금 요청은 잠금의 잠금 횟수를 늘립니다.

판독기와 작성기는 별도로 큐에 추가될 수 있습니다. 하나의 스레드가 작성기 잠금을 해제하면 해당 시점에 판독기 큐에서 대기하고 있던 모든 스레드에 판독기 잠금이 부여됩니다. 모든 판독기 잠금이 해제되면 작성기 큐에서 대기하고 있던 다음 스레드에 작성기 잠금이 부여됩니다. 즉, ReaderWriterLock은 판독기 컬렉션과 하나의 작성기 간에 교대로 작동합니다.

작성기 큐의 스레드가 활성 판독기 잠금이 해제되기를 기다리는 동안 새로운 판독기 잠금을 요청한 스레드가 판독기 큐에 추가됩니다. 이러한 스레드는 기존의 판독기 잠금 소유자와 동시 액세스 권한을 공유할 수 있지만 해당 요청이 부여되지 않습니다. 따라서 판독기가 작성기를 무제한 차단하는 문제를 피할 수 있습니다.

ReaderWriterLock 잠금을 설정하는 대부분의 메서드에는 제한 시간 값이 적용됩니다. 제한 시간을 사용하면 응용 프로그램의 교착 상태를 피할 수 있습니다. 예를 들어, 스레드는 하나의 리소스에 대해 작성기 잠금을 가져온 다음 두 번째 리소스에 대해 판독기 잠금을 요청할 수 있습니다. 그러는 동안 다른 스레드는 두 번째 리소스에 대해 작성기 잠금을 가져오고 첫 번째 리소스에 대해 판독기 잠금을 요청할 수 있습니다. 제한 시간이 사용되지 않으면 스레드 교착 상태가 발생합니다.

제한 시간 간격이 만료되었는데 잠금 요청이 부여되지 않았으면 이 메서드는 ApplicationException을 throw하여 호출 스레드로 제어를 반환합니다. 스레드는 이 예외를 catch하고 다음에 수행해야 할 작업을 결정합니다.

제한 시간 값은 밀리초 단위로 표시합니다. System.TimeSpan을 사용하여 제한 시간을 지정하는 경우 사용되는 값은 TimeSpan으로 나타낸 정수(밀리초)입니다. 다음 표에서는 유효한 제한 시간 값을 밀리초 단위로 나타냅니다.

Value

설명

-1

Infinite.

0

제한 시간이 없습니다.

> 0

대기하는 밀리초 수입니다.

-1을 제외한 음수 제한 시간 값은 허용되지 않습니다. -1 이외의 음의 정수를 사용하여 제한 시간을 지정하면 0(제한 시간 없음)이 사용됩니다. -1 이외의 음수(밀리초)를 나타내는 TimeSpan을 지정하면 ArgumentOutOfRangeException이 throw됩니다.

// This example shows a ReaderWriterLock protecting a shared
// resource that is read concurrently and written exclusively
// by multiple threads.

// The complete code is located in the ReaderWriterLock
// class topic.
using System;
using System.Threading;

public class Test
{
    // Declaring the ReaderWriterLock at the class level
    // makes it visible to all threads.
    static ReaderWriterLock rwl = new ReaderWriterLock();
    // For this example, the shared resource protected by the
    // ReaderWriterLock is just an integer.
    static int resource = 0;

    const int numThreads = 26;
    static bool running = true;
    static Random rnd = new Random();

    // Statistics.
    static int readerTimeouts = 0;
    static int writerTimeouts = 0;
    static int reads = 0;
    static int writes = 0;

    public static void Main(string[] args)
    {
        // Start a series of threads. Each thread randomly
        // performs reads and writes on the shared resource.
        Thread[] t = new Thread[numThreads];
        for (int i = 0; i < numThreads; i++)
        {
            t[i] = new Thread(new ThreadStart(ThreadProc));
            t[i].Name = new String(Convert.ToChar(i + 65), 1);
            t[i].Start();
            if (i > 10)
                Thread.Sleep(300);
        }

        // Tell the threads to shut down, then wait until they all
        // finish.
        running = false;
        for (int i = 0; i < numThreads; i++)
        {
            t[i].Join();
        }

        // Display statistics.
        Console.WriteLine("\r\n{0} reads, {1} writes, {2} reader time-outs, {3} writer time-outs.",
            reads, writes, readerTimeouts, writerTimeouts);
        Console.WriteLine("Press ENTER to exit.");
        Console.ReadLine();
    }

    static void ThreadProc()
    {
        // As long as a thread runs, it randomly selects
        // various ways to read and write from the shared 
        // resource. Each of the methods demonstrates one 
        // or more features of ReaderWriterLock.
        while (running)
        {
            double action = rnd.NextDouble();
            if (action < .8)
                ReadFromResource(10);
            else if (action < .81)
                ReleaseRestore(50);
            else if (action < .90)
                UpgradeDowngrade(100);
            else
                WriteToResource(100);
        }
    }

    // Shows how to request and release a reader lock, and
    // how to handle time-outs.
    static void ReadFromResource(int timeOut)
    {
        try
        {
            rwl.AcquireReaderLock(timeOut);
            try
            {
                // It is safe for this thread to read from
                // the shared resource.
                Display("reads resource value " + resource); 
                Interlocked.Increment(ref reads);
            }        
            finally
            {
                // Ensure that the lock is released.
                rwl.ReleaseReaderLock();
            }
        }
        catch (ApplicationException)
        {
            // The reader lock request timed out.
            Interlocked.Increment(ref readerTimeouts);
        }
    }

    // Shows how to request and release the writer lock, and
    // how to handle time-outs.
    static void WriteToResource(int timeOut)
    {
        try
        {
            rwl.AcquireWriterLock(timeOut);
            try
            {
                // It is safe for this thread to read or write
                // from the shared resource.
                resource = rnd.Next(500);
                Display("writes resource value " + resource);
                Interlocked.Increment(ref writes);
            }        
            finally
            {
                // Ensure that the lock is released.
                rwl.ReleaseWriterLock();
            }
        }
        catch (ApplicationException)
        {
            // The writer lock request timed out.
            Interlocked.Increment(ref writerTimeouts);
        }
    }

    // Shows how to request a reader lock, upgrade the
    // reader lock to the writer lock, and downgrade to a
    // reader lock again.
    static void UpgradeDowngrade(int timeOut)
    {
        try
        {
            rwl.AcquireReaderLock(timeOut);
            try
            {
                // It is safe for this thread to read from
                // the shared resource.
                Display("reads resource value " + resource); 
                Interlocked.Increment(ref reads);

                // If it is necessary to write to the resource,
                // you must either release the reader lock and 
                // then request the writer lock, or upgrade the
                // reader lock. Note that upgrading the reader lock
                // puts the thread in the write queue, behind any
                // other threads that might be waiting for the 
                // writer lock.
                try
                {
                    LockCookie lc = rwl.UpgradeToWriterLock(timeOut);
                    try
                    {
                        // It is safe for this thread to read or write
                        // from the shared resource.
                        resource = rnd.Next(500);
                        Display("writes resource value " + resource);
                        Interlocked.Increment(ref writes);
                    }        
                    finally
                    {
                        // Ensure that the lock is released.
                        rwl.DowngradeFromWriterLock(ref lc);
                    }
                }
                catch (ApplicationException)
                {
                    // The upgrade request timed out.
                    Interlocked.Increment(ref writerTimeouts);
                }

                // When the lock has been downgraded, it is 
                // still safe to read from the resource.
                Display("reads resource value " + resource); 
                Interlocked.Increment(ref reads);
            }        
            finally
            {
                // Ensure that the lock is released.
                rwl.ReleaseReaderLock();
            }
        }
        catch (ApplicationException)
        {
            // The reader lock request timed out.
            Interlocked.Increment(ref readerTimeouts);
        }
    }

    // Shows how to release all locks and later restore
    // the lock state. Shows how to use sequence numbers
    // to determine whether another thread has obtained
    // a writer lock since this thread last accessed the
    // resource.
    static void ReleaseRestore(int timeOut)
    {
        int lastWriter;

        try
        {
            rwl.AcquireReaderLock(timeOut);
            try
            {
                // It is safe for this thread to read from
                // the shared resource. Cache the value. (You
                // might do this if reading the resource is
                // an expensive operation.)
                int resourceValue = resource;
                Display("reads resource value " + resourceValue); 
                Interlocked.Increment(ref reads);

                // Save the current writer sequence number.
                lastWriter = rwl.WriterSeqNum;

                // Release the lock, and save a cookie so the
                // lock can be restored later.
                LockCookie lc = rwl.ReleaseLock();

                // Wait for a random interval (up to a 
                // quarter of a second), and then restore
                // the previous state of the lock. Note that
                // there is no time-out on the Restore method.
                Thread.Sleep(rnd.Next(250));
                rwl.RestoreLock(ref lc);

                // Check whether other threads obtained the
                // writer lock in the interval. If not, then
                // the cached value of the resource is still
                // valid.
                if (rwl.AnyWritersSince(lastWriter))
                {
                    resourceValue = resource;
                    Interlocked.Increment(ref reads);
                    Display("resource has changed " + resourceValue);
                }
                else
                {
                    Display("resource has not changed " + resourceValue);
                }
            }        
            finally
            {
                // Ensure that the lock is released.
                rwl.ReleaseReaderLock();
            }
        }
        catch (ApplicationException)
        {
            // The reader lock request timed out.
            Interlocked.Increment(ref readerTimeouts);
        }
    }

    // Helper method briefly displays the most recent
    // thread action. Comment out calls to Display to 
    // get a better idea of throughput.
    static void Display(string msg)
    {
        Console.Write("Thread {0} {1}.       \r", Thread.CurrentThread.Name, msg);
    }
}

// This example shows a ReaderWriterLock protecting a shared
// resource that is read concurrently and written exclusively
// by multiple threads.

// The complete code is located in the ReaderWriterLock
// class topic.
import System.*;
import System.Threading.*;
import System.Threading.Thread;    

public class Test
{
    // Declaring the ReaderWriterLock at the class level
    // makes it visible to all threads.
    private static ReaderWriterLock rwl = new ReaderWriterLock();

    // For this example, the shared resource protected by the
    // ReaderWriterLock is just an integer.
    private static int resource = 0;
    private static int numThreads = 26;
    private static boolean running = true;
    private static Random rnd = new Random();

    // Statistics.
    private static int readerTimeouts = 0;
    private static int writerTimeouts = 0;
    private static int reads = 0;
    private static int writes = 0;

    public static void main(String[] args)
    {
        // Start a series of threads. Each thread randomly
        // performs reads and writes on the shared resource.
        Thread t[] = new Thread[numThreads];

        for (int i = 0; i < numThreads; i++) {
            t[i] = new Thread(new ThreadStart(ThreadProc));
            t[i].set_Name(new String(Convert.ToChar((i + 65)), 1));
            t[i].Start();
            if (i > 10) {
                Thread.Sleep(300);
        }
    } //main

    // Tell the threads to shut down, then wait until they all
    // finish.
    running = false;
    for (int i = 0; i < numThreads; i++) {
        t[i].Join();
    }

    // Display statistics.
    Console.WriteLine("\r\n{0} reads, {1} writes, {2} reader time-outs," 
        + " {3} writer time-outs.",
        new Object[] { (Int32)(reads), (Int32)(writes), 
        (Int32)(readerTimeouts), (Int32)(writerTimeouts) });
    Console.WriteLine("Press ENTER to exit.");
    Console.ReadLine();
} //Test


    static void ThreadProc()
    {
        // As long as a thread runs, it randomly selects
        // various ways to read and write from the shared 
        // resource. Each of the methods demonstrates one 
        // or more features of ReaderWriterLock.
        while (running) {
            double action = rnd.NextDouble();

            if (action < 0.8) {
                ReadFromResource(10);
            }
            else {
                if (action < 0.81) {
                    ReleaseRestore(50);
                }
                else {
                    if (action < 0.9) {
                        UpgradeDowngrade(100);
                    }
                    else {
                        WriteToResource(100);
                    }
                }
            }
        }
    } //ThreadProc

    // Shows how to request and release a reader lock, and
    // how to handle time-outs.
    static void ReadFromResource(int timeOut)
    {
        try {
            rwl.AcquireReaderLock(timeOut);
            try {
                // It is safe for this thread to read from
                // the shared resource.
                Display(("reads resource value " + resource));
                Interlocked.Increment(reads);
            }
            finally {
                // Ensure that the lock is released.
                rwl.ReleaseReaderLock();
            }
        }
        catch (ApplicationException exp) {
            // The reader lock request timed out.
            Interlocked.Increment(readerTimeouts);
        }
    } //ReadFromResource

    // Shows how to request and release the writer lock, and
    // how to handle time-outs.
    static void WriteToResource(int timeOut)
    {
        try {
            rwl.AcquireWriterLock(timeOut);
            try {
                // It is safe for this thread to read or write
                // from the shared resource.
                resource = rnd.Next(500);
                Display(("writes resource value " + resource));
                Interlocked.Increment(writes);
            }
            finally {
                // Ensure that the lock is released.
                rwl.ReleaseWriterLock();
            }
        }
        catch (ApplicationException exp) {
            // The writer lock request timed out.
            Interlocked.Increment(writerTimeouts);
        }
    } //WriteToResource

    // Shows how to request a reader lock, upgrade the
    // reader lock to the writer lock, and downgrade to a
    // reader lock again.
    static void UpgradeDowngrade(int timeOut)
    {
        try {
            rwl.AcquireReaderLock(timeOut);
            try {
                // It is safe for this thread to read from
                // the shared resource.
                Display(("reads resource value " + resource));
                Interlocked.Increment(reads);

                // If it is necessary to write to the resource,
                // you must either release the reader lock and 
                // then request the writer lock, or upgrade the
                // reader lock. Note that upgrading the reader lock
                // puts the thread in the write queue, behind any
                // other threads that might be waiting for the 
                // writer lock.
                try {
                    LockCookie lc = rwl.UpgradeToWriterLock(timeOut);

                    try {
                        // It is safe for this thread to read or write
                        // from the shared resource.
                        resource = rnd.Next(500);
                        Display(("writes resource value " + resource));
                        Interlocked.Increment(writes);
                    }
                    finally {
                        // Ensure that the lock is released.
                        rwl.DowngradeFromWriterLock(lc);
                    }
                }
                catch (ApplicationException exp) {
                    // The upgrade request timed out.
                    Interlocked.Increment(writerTimeouts);
                }

                // When the lock has been downgraded, it is 
                // still safe to read from the resource.
                Display(("reads resource value " + resource));
                Interlocked.Increment(reads);
            }
            finally {
                // Ensure that the lock is released.
                rwl.ReleaseReaderLock();
            }
        }
        catch (ApplicationException exp) {
            // The reader lock request timed out.
            Interlocked.Increment(readerTimeouts);
        }
    } //UpgradeDowngrade


    // Shows how to release all locks and later restore
    // the lock state. Shows how to use sequence numbers
    // to determine whether another thread has obtained
    // a writer lock since this thread last accessed the
    // resource.
    static void ReleaseRestore(int timeOut)
    {
        int lastWriter;

        try {
            rwl.AcquireReaderLock(timeOut);
            try {
                // It is safe for this thread to read from
                // the shared resource. Cache the value. (You
                // might do this if reading the resource is
                // an expensive operation.)
                int resourceValue = resource;

                Display(("reads resource value " + resourceValue));
                Interlocked.Increment(reads);

                // Save the current writer sequence number.
                lastWriter = rwl.get_WriterSeqNum();

                // Release the lock, and save a cookie so the
                // lock can be restored later.
                LockCookie lc = rwl.ReleaseLock();

                // Wait for a random interval (up to a 
                // quarter of a second), and then restore
                // the previous state of the lock. Note that
                // there is no time-out on the Restore method.
                Thread.Sleep(rnd.Next(250));
                rwl.RestoreLock(lc);

                // Check whether other threads obtained the
                // writer lock in the interval. If not, then
                // the cached value of the resource is still
                // valid.
                if (rwl.AnyWritersSince(lastWriter)) {
                    resourceValue = resource;
                    Interlocked.Increment(reads);
                    Display(("resource has changed " + resourceValue));
                }
                else {
                    Display(("resource has not changed " + resourceValue));
                }
            }
            finally {
                // Ensure that the lock is released.
                rwl.ReleaseReaderLock();
            }
        }
        catch (ApplicationException exp) {
            // The reader lock request timed out.
            Interlocked.Increment(readerTimeouts);
        }
    } //ReleaseRestore

    // Helper method briefly displays the most recent
    // thread action. Comment out calls to Display to 
    // get a better idea of throughput.
    static void Display(String msg)
    {
        Console.Write("Thread {0} {1}.       \r",
            Thread.get_CurrentThread().get_Name(), msg);
    } //Display
}

이 형식은 다중 스레드 작업을 수행하는 데 안전합니다.

Windows 98, Windows 2000 SP4, Windows Millennium Edition, Windows Server 2003, Windows XP Media Center Edition, Windows XP Professional x64 Edition, Windows XP SP2, Windows XP Starter Edition

.NET Framework에서 모든 플래폼의 모든 버전을 지원하지는 않습니다. 지원되는 버전의 목록은 시스템 요구 사항을 참조하십시오.

.NET Framework

2.0, 1.1, 1.0에서 지원

커뮤니티 추가 항목

추가
표시:
© 2014 Microsoft