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>) . 또한 생성자(지정 isThreadSafe``true 대상) 및 Lazy<T>(Func<T>, LazyThreadSafetyMode) 생성자(LazyThreadSafetyMode.ExecutionAndPublicationmode지정)의 Lazy<T>(Func<T>, Boolean) 사용도 보여 줍니다. 다른 생성자로 전환하려면 주석 처리되는 생성자를 변경하기만 하면됩니다.

동일한 생성자를 사용하는 예외 캐싱을 보여 주는 예제는 생성자를 참조 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>사용하며 모든 메서드는 (SharedVisual Basic)입니다 static . 지연 초기화를 사용하기 위한 요구 사항은 아닙니다.

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>)
하나의 스레드 isThreadSafefalse으로 설정된 Lazy<T>(Boolean) isThreadSafefalse으로 설정된 Lazy<T>(Func<T>, Boolean)

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

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

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

중요

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

동등한 생성자 사용을 사용하도록 설정하는 LazyThreadSafetyMode.PublicationOnlyLazy<T>(LazyThreadSafetyMode) 것 외에도 생성 Lazy<T>(Func<T>, LazyThreadSafetyMode) 자와 생성자는 다른 생성자의 기능을 복제할 수 있습니다. 다음 표에는 동등한 동작을 생성하는 매개 변수 값이 표시됩니다.

개체를 Lazy<T> 만들려면 매개 변수 mode 가 있는 LazyThreadSafetyMode 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> 모든 공용 및 보호된 멤버는 스레드로부터 안전하며 여러 스레드에서 동시에 사용될 수 있습니다. 이러한 스레드 안전 보장은 형식의 생성자에 대한 매개 변수를 사용하여 인스턴스별로 선택적으로 제거될 수 있습니다.

추가 정보