다음을 통해 공유


Lazy<T> 클래스

정의

지연 초기화를 지원합니다.

generic <typename T>
public ref class Lazy
public class Lazy<T>
[System.Runtime.InteropServices.ComVisible(false)]
[System.Serializable]
public class Lazy<T>
type Lazy<'T> = class
[<System.Runtime.InteropServices.ComVisible(false)>]
[<System.Serializable>]
type Lazy<'T> = class
Public Class Lazy(Of T)

형식 매개 변수

T

지연 초기화되는 개체의 형식입니다.

상속
Lazy<T>
파생
특성

예제

다음 예제에서는 Lazy<T> 클래스를 사용하여 여러 스레드에서 액세스할 수 있는 지연 초기화를 제공하는 방법을 보여 줍니다.

메모

이 예제에서는 Lazy<T>(Func<T>) 생성자를 사용합니다. 또한 Lazy<T>(Func<T>, Boolean) 생성자(isThreadSafetrue 지정) 및 Lazy<T>(Func<T>, LazyThreadSafetyMode) 생성자(modeLazyThreadSafetyMode.ExecutionAndPublication 지정)의 사용도 보여 줍니다. 다른 생성자로 전환하려면 주석 처리되는 생성자를 변경하기만 하면됩니다.

동일한 생성자를 사용하여 예외 캐싱을 보여 주는 예제는 Lazy<T>(Func<T>) 생성자를 참조하세요.

이 예제에서는 여러 스레드 중 하나에 의해 지연 초기화될 LargeObject 클래스를 정의합니다. 코드의 네 가지 주요 섹션에서는 개체를 만들 때 메시지를 표시하는 이니셜라이저, 팩터리 메서드, 실제 초기화 및 LargeObject 클래스의 생성자를 만드는 방법을 보여 줍니다. Main 메서드의 시작 부분에서 예제에서는 LargeObject스레드로부터 안전한 지연 이니셜라이저를 만듭니다.

lazyLargeObject = new Lazy<LargeObject>(InitLargeObject);

// The following lines show how to use other constructors to achieve exactly the
// same result as the previous line:
//lazyLargeObject = new Lazy<LargeObject>(InitLargeObject, true);
//lazyLargeObject = new Lazy<LargeObject>(InitLargeObject,
//                               LazyThreadSafetyMode.ExecutionAndPublication);
let lazyLargeObject = Lazy<LargeObject> initLargeObject

// The following lines show how to use other constructors to achieve exactly the
// same result as the previous line:
//     let lazyLargeObject = Lazy<LargeObject>(initLargeObject, true)
//     let lazyLargeObject = Lazy<LargeObject>(initLargeObject,
//                               LazyThreadSafetyMode.ExecutionAndPublication)
lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject)

' The following lines show how to use other constructors to achieve exactly the
' same result as the previous line: 
'lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject, True)
'lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject, _
'                               LazyThreadSafetyMode.ExecutionAndPublication)

팩터리 메서드는 추가 초기화를 위한 자리 표시자를 사용하여 개체를 만드는 방법을 보여 줍니다.

static LargeObject InitLargeObject()
{
    LargeObject large = new LargeObject(Thread.CurrentThread.ManagedThreadId);
    // Perform additional initialization here.
    return large;
}
let initLargeObject () =
    let large = LargeObject Thread.CurrentThread.ManagedThreadId
    // Perform additional initialization here.
    large
Private Shared Function InitLargeObject() As LargeObject
    Dim large As New LargeObject(Thread.CurrentThread.ManagedThreadId)
    ' Perform additional initialization here.
    Return large
End Function

처음 두 코드 섹션은 다음과 같이 람다 함수를 사용하여 결합할 수 있습니다.

lazyLargeObject = new Lazy<LargeObject>(() =>
{
    LargeObject large = new LargeObject(Thread.CurrentThread.ManagedThreadId);
    // Perform additional initialization here.
    return large;
});
let lazyLargeObject = Lazy<LargeObject>(fun () ->
    let large = LargeObject Thread.CurrentThread.ManagedThreadId
    // Perform additional initialization here.
    large)
lazyLargeObject = New Lazy(Of LargeObject)(Function () 
    Dim large As New LargeObject(Thread.CurrentThread.ManagedThreadId) 
    ' Perform additional initialization here.
    Return large
End Function)

지연 초기화가 발생하기 전에 확정되지 않은 기간이 경과할 수 있음을 나타내기 위해 이 예제가 일시 중지됩니다. Enter 키를 누르면 세 개의 스레드가 만들어지고 시작됩니다. 세 스레드 모두에 사용되는 ThreadProc 메서드는 Value 속성을 호출합니다. 이 경우 처음으로 LargeObject 인스턴스가 만들어집니다.

LargeObject large = lazyLargeObject.Value;

// IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
//            object after creation. You must lock the object before accessing it,
//            unless the type is thread safe. (LargeObject is not thread safe.)
lock(large)
{
    large.Data[0] = Thread.CurrentThread.ManagedThreadId;
    Console.WriteLine("Initialized by thread {0}; last used by thread {1}.",
        large.InitializedBy, large.Data[0]);
}
let large = lazyLargeObject.Value

// IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
//            object after creation. You must lock the object before accessing it,
//            unless the type is thread safe. (LargeObject is not thread safe.)
lock large (fun () ->
    large.Data[0] <- Thread.CurrentThread.ManagedThreadId
    printfn $"Initialized by thread {large.InitializedBy} last used by thread {large.Data[0]}.")
Dim large As LargeObject = lazyLargeObject.Value

' IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the  
'            object after creation. You must lock the object before accessing it,
'            unless the type is thread safe. (LargeObject is not thread safe.)
SyncLock large
    large.Data(0) = Thread.CurrentThread.ManagedThreadId
    Console.WriteLine("Initialized by thread {0}; last used by thread {1}.", _
        large.InitializedBy, large.Data(0))
End SyncLock

코드의 마지막 키 섹션을 포함하는 LargeObject 클래스의 생성자는 메시지를 표시하고 초기화 스레드의 ID를 기록합니다. 프로그램의 출력은 전체 코드 목록의 끝에 표시됩니다.

int initBy = 0;
public LargeObject(int initializedBy)
{
    initBy = initializedBy;
    Console.WriteLine("LargeObject was created on thread id {0}.", initBy);
}
type LargeObject(initBy) =
    do 
        printfn $"LargeObject was created on thread id %i{initBy}."
Private initBy As Integer = 0
Public Sub New(ByVal initializedBy As Integer)
    initBy = initializedBy
    Console.WriteLine("LargeObject was created on thread id {0}.", initBy)
End Sub

메모

간단히 하기 위해 이 예제에서는 Lazy<T>전역 인스턴스를 사용하고 모든 메서드는 static(Visual Basic에서는Shared)입니다. 지연 초기화를 사용하기 위한 요구 사항은 아닙니다.

using System;
using System.Threading;

class Program
{
    static Lazy<LargeObject> lazyLargeObject = null;

    static LargeObject InitLargeObject()
    {
        LargeObject large = new LargeObject(Thread.CurrentThread.ManagedThreadId);
        // Perform additional initialization here.
        return large;
    }

    static void Main()
    {
        // The lazy initializer is created here. LargeObject is not created until the
        // ThreadProc method executes.
        lazyLargeObject = new Lazy<LargeObject>(InitLargeObject);

        // The following lines show how to use other constructors to achieve exactly the
        // same result as the previous line:
        //lazyLargeObject = new Lazy<LargeObject>(InitLargeObject, true);
        //lazyLargeObject = new Lazy<LargeObject>(InitLargeObject,
        //                               LazyThreadSafetyMode.ExecutionAndPublication);

        Console.WriteLine(
            "\r\nLargeObject is not created until you access the Value property of the lazy" +
            "\r\ninitializer. Press Enter to create LargeObject.");
        Console.ReadLine();

        // Create and start 3 threads, each of which uses LargeObject.
        Thread[] threads = new Thread[3];
        for (int i = 0; i < 3; i++)
        {
            threads[i] = new Thread(ThreadProc);
            threads[i].Start();
        }

        // Wait for all 3 threads to finish.
        foreach (Thread t in threads)
        {
            t.Join();
        }

        Console.WriteLine("\r\nPress Enter to end the program");
        Console.ReadLine();
    }

    static void ThreadProc(object state)
    {
        LargeObject large = lazyLargeObject.Value;

        // IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
        //            object after creation. You must lock the object before accessing it,
        //            unless the type is thread safe. (LargeObject is not thread safe.)
        lock(large)
        {
            large.Data[0] = Thread.CurrentThread.ManagedThreadId;
            Console.WriteLine("Initialized by thread {0}; last used by thread {1}.",
                large.InitializedBy, large.Data[0]);
        }
    }
}

class LargeObject
{
    public int InitializedBy { get { return initBy; } }

    int initBy = 0;
    public LargeObject(int initializedBy)
    {
        initBy = initializedBy;
        Console.WriteLine("LargeObject was created on thread id {0}.", initBy);
    }

    public long[] Data = new long[100000000];
}

/* This example produces output similar to the following:

LargeObject is not created until you access the Value property of the lazy
initializer. Press Enter to create LargeObject.

LargeObject was created on thread id 3.
Initialized by thread 3; last used by thread 3.
Initialized by thread 3; last used by thread 4.
Initialized by thread 3; last used by thread 5.

Press Enter to end the program
 */
open System
open System.Threading

type LargeObject(initBy) =
    do 
        printfn $"LargeObject was created on thread id %i{initBy}."
    member _.InitializedBy = initBy
    member val Data = Array.zeroCreate<int64> 100000000

let initLargeObject () =
    let large = LargeObject Thread.CurrentThread.ManagedThreadId
    // Perform additional initialization here.
    large

// The lazy initializer is created here. LargeObject is not created until the
// ThreadProc method executes.
let lazyLargeObject = Lazy<LargeObject> initLargeObject

// The following lines show how to use other constructors to achieve exactly the
// same result as the previous line:
//     let lazyLargeObject = Lazy<LargeObject>(initLargeObject, true)
//     let lazyLargeObject = Lazy<LargeObject>(initLargeObject,
//                               LazyThreadSafetyMode.ExecutionAndPublication)

let threadProc (state: obj) =
    let large = lazyLargeObject.Value

    // IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
    //            object after creation. You must lock the object before accessing it,
    //            unless the type is thread safe. (LargeObject is not thread safe.)
    lock large (fun () ->
        large.Data[0] <- Thread.CurrentThread.ManagedThreadId
        printfn $"Initialized by thread {large.InitializedBy} last used by thread {large.Data[0]}.")

printfn """
LargeObject is not created until you access the Value property of the lazy
initializer. Press Enter to create LargeObject."""
stdin.ReadLine() |> ignore

// Create and start 3 threads, each of which uses LargeObject.

let threads = Array.zeroCreate 3
for i = 0 to 2 do
    threads[i] <- Thread(ParameterizedThreadStart threadProc)
    threads[i].Start()

// Wait for all 3 threads to finish.
for t in threads do
    t.Join()

printfn "\nPress Enter to end the program"
stdin.ReadLine() |> ignore

// This example produces output similar to the following:
//     LargeObject is not created until you access the Value property of the lazy
//     initializer. Press Enter to create LargeObject.
//     
//     LargeObject was created on thread id 3.
//     Initialized by thread 3 last used by thread 3.
//     Initialized by thread 3 last used by thread 4.
//     Initialized by thread 3 last used by thread 5.
//     
//     Press Enter to end the program
Imports System.Threading

Friend Class Program
    Private Shared lazyLargeObject As Lazy(Of LargeObject) = Nothing

    Private Shared Function InitLargeObject() As LargeObject
        Dim large As New LargeObject(Thread.CurrentThread.ManagedThreadId)
        ' Perform additional initialization here.
        Return large
    End Function


    Shared Sub Main()
        ' The lazy initializer is created here. LargeObject is not created until the 
        ' ThreadProc method executes.
        lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject)

        ' The following lines show how to use other constructors to achieve exactly the
        ' same result as the previous line: 
        'lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject, True)
        'lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject, _
        '                               LazyThreadSafetyMode.ExecutionAndPublication)


        Console.WriteLine(vbCrLf & _
            "LargeObject is not created until you access the Value property of the lazy" _
            & vbCrLf & "initializer. Press Enter to create LargeObject.")
        Console.ReadLine()

        ' Create and start 3 threads, each of which uses LargeObject.
        Dim threads(2) As Thread
        For i As Integer = 0 To 2
            threads(i) = New Thread(AddressOf ThreadProc)
            threads(i).Start()
        Next i

        ' Wait for all 3 threads to finish. 
        For Each t As Thread In threads
            t.Join()
        Next t

        Console.WriteLine(vbCrLf & "Press Enter to end the program")
        Console.ReadLine()
    End Sub


    Private Shared Sub ThreadProc(ByVal state As Object)
        Dim large As LargeObject = lazyLargeObject.Value

        ' IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the  
        '            object after creation. You must lock the object before accessing it,
        '            unless the type is thread safe. (LargeObject is not thread safe.)
        SyncLock large
            large.Data(0) = Thread.CurrentThread.ManagedThreadId
            Console.WriteLine("Initialized by thread {0}; last used by thread {1}.", _
                large.InitializedBy, large.Data(0))
        End SyncLock
    End Sub
End Class

Friend Class LargeObject
    Public ReadOnly Property InitializedBy() As Integer
        Get
            Return initBy
        End Get
    End Property

    Private initBy As Integer = 0
    Public Sub New(ByVal initializedBy As Integer)
        initBy = initializedBy
        Console.WriteLine("LargeObject was created on thread id {0}.", initBy)
    End Sub

    Public Data(99999999) As Long
End Class

' This example produces output similar to the following:
'
'LargeObject is not created until you access the Value property of the lazy
'initializer. Press Enter to create LargeObject.
'
'LargeObject was created on thread id 3.
'Initialized by thread 3; last used by thread 3.
'Initialized by thread 3; last used by thread 5.
'Initialized by thread 3; last used by thread 4.
'
'Press Enter to end the program
'

설명

지연 초기화를 사용하여 대규모 또는 리소스 집약적 개체 만들기 또는 리소스 집약적 작업의 실행을 연기합니다. 특히 프로그램 수명 동안 이러한 생성 또는 실행이 발생하지 않을 수 있습니다.

지연 초기화를 준비하려면 Lazy<T>인스턴스를 만듭니다. 만드는 Lazy<T> 개체의 형식 인수는 지연 초기화하려는 개체의 형식을 지정합니다. Lazy<T> 개체를 만드는 데 사용하는 생성자는 초기화의 특성을 결정합니다. 지연 초기화는 Lazy<T>.Value 속성에 처음 액세스할 때 발생합니다.

대부분의 경우 생성자를 선택하는 것은 다음 두 가지 질문에 대한 답변에 따라 달라집니다.

  • 지연 초기화된 개체가 둘 이상의 스레드에서 액세스되나요? 이 경우 Lazy<T> 개체가 모든 스레드에서 만들 수 있습니다. 스레드로부터 안전한 Lazy<T> 개체를 만드는 것이 기본 동작인 간단한 생성자 중 하나를 사용하면 스레드 수에 관계없이 지연 인스턴스화된 개체의 인스턴스가 하나만 만들어집니다. 스레드로부터 안전하지 않은 Lazy<T> 개체를 만들려면 스레드 안전성 없음을 지정할 수 있는 생성자를 사용해야 합니다.

    주의

    Lazy<T> 개체 스레드를 안전하게 만들면 지연 초기화된 개체가 보호되지 않습니다. 여러 스레드가 지연 초기화된 개체에 액세스할 수 있는 경우 다중 스레드 액세스에 대해 해당 속성과 메서드를 안전하게 만들어야 합니다.

  • 지연 초기화에는 많은 코드가 필요합니까, 아니면 지연 초기화된 개체에 필요한 모든 작업을 수행하고 예외를 throw하지 않는 매개 변수가 없는 생성자가 있나요? 초기화 코드를 작성해야 하거나 예외를 처리해야 하는 경우 팩터리 메서드를 사용하는 생성자 중 하나를 사용합니다. 초기화 코드를 팩터리 메서드에 작성합니다.

다음 표에서는 다음 두 요인에 따라 선택할 생성자를 보여 줍니다.

개체에 액세스할 수 있습니다. 초기화 코드가 필요하지 않은 경우(매개 변수 없는 생성자) 사용 초기화 코드가 필요한 경우
여러 스레드 Lazy<T>() Lazy<T>(Func<T>)
하나의 스레드 isThreadSafe false설정하여 Lazy<T>(Boolean). isThreadSafe false설정하여 Lazy<T>(Func<T>, Boolean).

람다 식을 사용하여 팩터리 메서드를 지정할 수 있습니다. 이렇게 하면 모든 초기화 코드가 한 곳에 유지됩니다. 람다 식은 지연 초기화된 개체의 생성자에 전달하는 인수를 포함하여 컨텍스트를 캡처합니다.

예외 캐싱 팩터리 메서드를 사용하면 예외가 캐시됩니다. 즉, 스레드가 Lazy<T> 개체의 Value 속성에 처음 액세스할 때 팩터리 메서드가 예외를 throw하는 경우 후속 시도마다 동일한 예외가 throw됩니다. 이렇게 하면 Value 속성에 대한 모든 호출이 동일한 결과를 생성하고 다른 스레드가 다른 결과를 얻을 경우 발생할 수 있는 미묘한 오류를 방지합니다. Lazy<T> 그렇지 않으면 일반적으로 시작 하는 동안 일부 이전 시점에서 초기화 되었을 것 이다 실제 T 대 한 의미. 이전 시점의 오류는 일반적으로 치명적입니다. 복구 가능한 오류가 발생할 가능성이 있는 경우 지연 초기화를 사용하지 않는 경우와 마찬가지로 초기화 루틴(이 경우 팩터리 메서드)에 재시도 논리를 빌드하는 것이 좋습니다.

잠금 대신 특정 상황에서는 Lazy<T> 개체의 기본 잠금 동작 오버헤드를 방지할 수 있습니다. 드물게 교착 상태가 발생할 수 있습니다. 이러한 경우 Lazy<T>(LazyThreadSafetyMode) 또는 Lazy<T>(Func<T>, LazyThreadSafetyMode) 생성자를 사용하고 LazyThreadSafetyMode.PublicationOnly지정할 수 있습니다. 이렇게 하면 스레드가 Value 속성을 동시에 호출하는 경우 Lazy<T> 개체가 여러 스레드 각각에 지연 초기화된 개체의 복사본을 만들 수 있습니다. Lazy<T> 개체는 모든 스레드가 지연 초기화된 개체의 동일한 인스턴스를 사용하고 사용되지 않는 인스턴스를 삭제합니다. 따라서 잠금 오버헤드를 줄이는 비용은 프로그램에서 비용이 많이 드는 개체의 추가 복사본을 만들고 삭제할 수 있다는 것입니다. 대부분의 경우, 이것은 가능성이 없습니다. Lazy<T>(LazyThreadSafetyMode)Lazy<T>(Func<T>, LazyThreadSafetyMode) 생성자에 대한 예제에서는 이 동작을 보여 줍니다.

중요하다

LazyThreadSafetyMode.PublicationOnly지정하면 팩터리 메서드를 지정하더라도 예외가 캐시되지 않습니다.

해당 생성자LazyThreadSafetyMode.PublicationOnly사용할 수 있는 것 외에도 Lazy<T>(LazyThreadSafetyMode)Lazy<T>(Func<T>, LazyThreadSafetyMode) 생성자는 다른 생성자의 기능을 복제할 수 있습니다. 다음 표에서는 동등한 동작을 생성하는 매개 변수 값을 보여 있습니다.

Lazy<T> 개체를 만들려면 LazyThreadSafetyMode mode 매개 변수가 있는 생성자의 경우 mode 부울 isThreadSafe 매개 변수가 있는 생성자의 경우 isThreadSafe 스레드 안전 매개 변수가 없는 생성자의 경우
완전 스레드로부터 안전합니다. 는 잠금을 사용하여 하나의 스레드만 값을 초기화하도록 합니다. ExecutionAndPublication true 이러한 모든 생성자는 완전히 스레드로부터 안전합니다.
스레드로부터 안전하지 않습니다. None false 적용할 수 없습니다.
완전 스레드로부터 안전합니다. 스레드는 값을 초기화하기 위해 경합합니다. PublicationOnly 적용할 수 없습니다. 적용할 수 없습니다.

기타 기능 스레드 정적 필드에 Lazy<T> 사용하거나 속성의 백업 저장소로 사용하는 방법에 대한 자세한 내용은 지연 초기화참조하세요.

생성자

Lazy<T>()

Lazy<T> 클래스의 새 인스턴스를 초기화합니다. 지연 초기화가 발생하면 대상 형식의 매개 변수가 없는 생성자가 사용됩니다.

Lazy<T>(Boolean)

Lazy<T> 클래스의 새 인스턴스를 초기화합니다. 지연 초기화가 발생하면 대상 형식의 매개 변수가 없는 생성자와 지정된 초기화 모드가 사용됩니다.

Lazy<T>(Func<T>)

Lazy<T> 클래스의 새 인스턴스를 초기화합니다. 지연 초기화가 발생하면 지정된 초기화 함수가 사용됩니다.

Lazy<T>(Func<T>, Boolean)

Lazy<T> 클래스의 새 인스턴스를 초기화합니다. 지연 초기화가 발생하면 지정된 초기화 함수 및 초기화 모드가 사용됩니다.

Lazy<T>(Func<T>, LazyThreadSafetyMode)

지정된 초기화 함수 및 스레드 안전 모드를 사용하는 Lazy<T> 클래스의 새 인스턴스를 초기화합니다.

Lazy<T>(LazyThreadSafetyMode)

T 매개 변수가 없는 생성자 및 지정된 스레드 안전 모드를 사용하는 Lazy<T> 클래스의 새 인스턴스를 초기화합니다.

Lazy<T>(T)

미리 초기화된 지정된 값을 사용하는 Lazy<T> 클래스의 새 인스턴스를 초기화합니다.

속성

IsValueCreated

Lazy<T> 인스턴스에 대해 값이 만들어졌는지 여부를 나타내는 값을 가져옵니다.

Value

현재 Lazy<T> 인스턴스의 지연 초기화 값을 가져옵니다.

메서드

Equals(Object)

지정된 개체가 현재 개체와 같은지 여부를 확인합니다.

(다음에서 상속됨 Object)
GetHashCode()

기본 해시 함수로 사용됩니다.

(다음에서 상속됨 Object)
GetType()

현재 인스턴스의 Type 가져옵니다.

(다음에서 상속됨 Object)
MemberwiseClone()

현재 Object단순 복사본을 만듭니다.

(다음에서 상속됨 Object)
ToString()

이 인스턴스에 대한 Value 속성의 문자열 표현을 만들고 반환합니다.

적용 대상

스레드 보안

기본적으로 Lazy<T> 클래스의 모든 public 및 protected 멤버는 스레드로부터 안전하며 여러 스레드에서 동시에 사용할 수 있습니다. 이러한 스레드 안전 보장은 형식의 생성자에 대한 매개 변수를 사용하여 인스턴스별로 선택적으로 제거될 수 있습니다.

추가 정보