Lazy<T> Classe
Definição
Importante
Algumas informações se referem a produtos de pré-lançamento que podem ser substancialmente modificados antes do lançamento. A Microsoft não oferece garantias, expressas ou implícitas, das informações aqui fornecidas.
Fornece suporte para inicialização lenta.
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)
Parâmetros de tipo
- T
O tipo de objeto que está sendo inicializado lentamente.
- Herança
-
Lazy<T>
- Derivado
- Atributos
Exemplos
O exemplo a seguir demonstra o uso da classe Lazy<T> para fornecer inicialização lenta com acesso de vários threads.
Nota
O exemplo usa o construtor Lazy<T>(Func<T>). Ele também demonstra o uso do construtor Lazy<T>(Func<T>, Boolean) (especificando true
para isThreadSafe
) e do construtor Lazy<T>(Func<T>, LazyThreadSafetyMode) (especificando LazyThreadSafetyMode.ExecutionAndPublication para mode
). Para alternar para um construtor diferente, basta alterar quais construtores são comentados.
Para obter um exemplo que demonstra o cache de exceções usando os mesmos construtores, consulte o construtor Lazy<T>(Func<T>).
O exemplo define uma classe LargeObject
que será inicializada de forma preguiçosa por um dos vários threads. As quatro seções principais do código ilustram a criação do inicializador, o método de fábrica, a inicialização real e o construtor da classe LargeObject
, que exibe uma mensagem quando o objeto é criado. No início do método Main
, o exemplo cria o inicializador lento de thread-safe para 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)
O método de fábrica mostra a criação do objeto, com um espaço reservado para inicialização adicional:
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
Observe que as duas primeiras seções de código podem ser combinadas usando uma função lambda, conforme mostrado aqui:
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)
O exemplo pausa, para indicar que um período indeterminado pode decorrer antes da inicialização lenta ocorrer. Quando você pressiona a tecla Enter, o exemplo cria e inicia três threads. O método ThreadProc
usado pelos três threads chama a propriedade Value. Na primeira vez que isso acontecer, a instância LargeObject
será criada:
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
O construtor da classe LargeObject
, que inclui a última seção de chave do código, exibe uma mensagem e registra a identidade do thread de inicialização. A saída do programa é exibida no final da listagem de código completa.
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
Nota
Para simplificar, este exemplo usa uma instância global de Lazy<T>e todos os métodos são static
(Shared
no Visual Basic). Esses não são requisitos para o uso da inicialização lenta.
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
'
Comentários
Use a inicialização lenta para adiar a criação de um objeto grande ou com uso intensivo de recursos ou a execução de uma tarefa com uso intensivo de recursos, especialmente quando essa criação ou execução pode não ocorrer durante o tempo de vida do programa.
Para se preparar para a inicialização lenta, crie uma instância de Lazy<T>. O argumento de tipo do objeto Lazy<T> que você cria especifica o tipo do objeto que você deseja inicializar preguiçosamente. O construtor que você usa para criar o objeto Lazy<T> determina as características da inicialização. A inicialização lenta ocorre na primeira vez em que a propriedade Lazy<T>.Value é acessada.
Na maioria dos casos, escolher um construtor depende de suas respostas para duas perguntas:
O objeto inicializado preguiçosamente será acessado de mais de um thread? Nesse caso, o objeto Lazy<T> poderá criá-lo em qualquer thread. Você pode usar um dos construtores simples cujo comportamento padrão é criar um objeto Lazy<T> thread-safe, de modo que apenas uma instância do objeto instanciado preguiçosamente seja criada, independentemente de quantos threads tentem acessá-lo. Para criar um objeto Lazy<T> que não seja thread-safe, você deve usar um construtor que permita que você não especifique nenhuma segurança de thread.
Cuidado
Tornar o thread de objeto Lazy<T> seguro não protege o objeto inicializado preguiçosamente. Se vários threads puderem acessar o objeto inicializado preguiçosamente, você deverá tornar suas propriedades e métodos seguros para acesso multithread.
A inicialização lenta requer muito código ou o objeto inicializado preguiçosamente tem um construtor sem parâmetros que faz tudo o que você precisa e não gera exceções? Se você precisar escrever o código de inicialização ou se as exceções precisarem ser tratadas, use um dos construtores que usa um método de fábrica. Escreva o código de inicialização no método de fábrica.
A tabela a seguir mostra qual construtor escolher, com base nesses dois fatores:
O objeto será acessado por | Se nenhum código de inicialização for necessário (construtor sem parâmetros), use | Se o código de inicialização for necessário, use |
---|---|---|
Vários threads | Lazy<T>() | Lazy<T>(Func<T>) |
Um thread |
Lazy<T>(Boolean) com isThreadSafe definido como false . |
Lazy<T>(Func<T>, Boolean) com isThreadSafe definido como false . |
Você pode usar uma expressão lambda para especificar o método de fábrica. Isso mantém todo o código de inicialização em um só lugar. A expressão lambda captura o contexto, incluindo os argumentos que você passa para o construtor do objeto inicializado preguiçosamente inicializado.
cache de exceção Quando você usa métodos de fábrica, as exceções são armazenadas em cache. Ou seja, se o método de fábrica gerar uma exceção na primeira vez que um thread tentar acessar a propriedade Value do objeto Lazy<T>, a mesma exceção será gerada em todas as tentativas subsequentes. Isso garante que cada chamada à propriedade Value produza o mesmo resultado e evita erros sutis que possam surgir se threads diferentes obtiverem resultados diferentes. O Lazy<T> representa uma T
real que, de outra forma, teria sido inicializada em algum momento anterior, geralmente durante a inicialização. Uma falha nesse ponto anterior geralmente é fatal. Se houver um potencial para uma falha recuperável, recomendamos que você crie a lógica de repetição na rotina de inicialização (nesse caso, o método de fábrica), assim como faria se não estivesse usando a inicialização lenta.
Alternativa ao bloqueio de Em determinadas situações, convém evitar a sobrecarga do comportamento de bloqueio padrão do objeto Lazy<T>. Em situações raras, pode haver um potencial para deadlocks. Nesses casos, você pode usar o construtor Lazy<T>(LazyThreadSafetyMode) ou Lazy<T>(Func<T>, LazyThreadSafetyMode) e especificar LazyThreadSafetyMode.PublicationOnly. Isso permite que o objeto Lazy<T> crie uma cópia do objeto inicializado preguiçosamente em cada um dos vários threads se os threads chamarem a propriedade Value simultaneamente. O objeto Lazy<T> garante que todos os threads usem a mesma instância do objeto inicializado preguiçosamente e descarta as instâncias que não são usadas. Portanto, o custo de reduzir a sobrecarga de bloqueio é que seu programa pode, às vezes, criar e descartar cópias extras de um objeto caro. Na maioria dos casos, isso é improvável. Os exemplos dos construtores Lazy<T>(LazyThreadSafetyMode) e Lazy<T>(Func<T>, LazyThreadSafetyMode) demonstram esse comportamento.
Importante
Quando você especifica LazyThreadSafetyMode.PublicationOnly, as exceções nunca são armazenadas em cache, mesmo se você especificar um método de fábrica.
Construtores equivalentes Além de habilitar o uso de LazyThreadSafetyMode.PublicationOnly, os construtores Lazy<T>(LazyThreadSafetyMode) e Lazy<T>(Func<T>, LazyThreadSafetyMode) podem duplicar a funcionalidade dos outros construtores. A tabela a seguir mostra os valores de parâmetro que produzem comportamento equivalente.
Para criar um objeto Lazy<T> que é | Para construtores que têm um parâmetro LazyThreadSafetyMode mode , defina mode como |
Para construtores que têm um parâmetro booliano isThreadSafe , defina isThreadSafe como |
Para construtores sem parâmetros de segurança de thread |
---|---|---|---|
Totalmente thread seguro; usa o bloqueio para garantir que apenas um thread inicialize o valor. | ExecutionAndPublication | true |
Todos esses construtores são totalmente thread-safe. |
Não thread-safe. | None | false |
Não aplicável. |
Totalmente thread seguro; threads correm para inicializar o valor. | PublicationOnly | Não aplicável. | Não aplicável. |
Outros recursos Para obter informações sobre o uso de Lazy<T> com campos estáticos de thread ou como repositório de backup para propriedades, consulte Inicialização Lenta.
Construtores
Lazy<T>() |
Inicializa uma nova instância da classe Lazy<T>. Quando ocorre uma inicialização lenta, o construtor sem parâmetros do tipo de destino é usado. |
Lazy<T>(Boolean) |
Inicializa uma nova instância da classe Lazy<T>. Quando ocorre uma inicialização lenta, o construtor sem parâmetros do tipo de destino e o modo de inicialização especificado são usados. |
Lazy<T>(Func<T>) |
Inicializa uma nova instância da classe Lazy<T>. Quando ocorre uma inicialização lenta, a função de inicialização especificada é usada. |
Lazy<T>(Func<T>, Boolean) |
Inicializa uma nova instância da classe Lazy<T>. Quando ocorre uma inicialização lenta, a função de inicialização especificada e o modo de inicialização são usados. |
Lazy<T>(Func<T>, LazyThreadSafetyMode) |
Inicializa uma nova instância da classe Lazy<T> que usa a função de inicialização especificada e o modo de segurança de thread. |
Lazy<T>(LazyThreadSafetyMode) |
Inicializa uma nova instância da classe Lazy<T> que usa o construtor sem parâmetros de |
Lazy<T>(T) |
Inicializa uma nova instância da classe Lazy<T> que usa um valor especificado pré-inicializado. |
Propriedades
IsValueCreated |
Obtém um valor que indica se um valor foi criado para essa instância Lazy<T>. |
Value |
Obtém o valor inicializado preguiçosamente da instância de Lazy<T> atual. |
Métodos
Equals(Object) |
Determina se o objeto especificado é igual ao objeto atual. (Herdado de Object) |
GetHashCode() |
Serve como a função de hash padrão. (Herdado de Object) |
GetType() |
Obtém o Type da instância atual. (Herdado de Object) |
MemberwiseClone() |
Cria uma cópia superficial do Objectatual. (Herdado de Object) |
ToString() |
Cria e retorna uma representação de cadeia de caracteres da propriedade Value para essa instância. |
Aplica-se a
Acesso thread-safe
Por padrão, todos os membros públicos e protegidos da classe Lazy<T> são thread-safe e podem ser usados simultaneamente de vários threads. Essas garantias de segurança de thread podem ser removidas opcionalmente e por instância, usando parâmetros para os construtores do tipo.
Confira também
- LazyThreadSafetyMode
- de inicialização lenta
- expressões lentas (F#)