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.
Dá suporte à 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 sem pressa.
- Herança
-
Lazy<T>
- Derivado
- Atributos
Exemplos
O exemplo a seguir demonstra o uso da Lazy<T> classe para fornecer inicialização lenta com acesso de vários threads.
Observação
O exemplo usa o Lazy<T>(Func<T>) construtor. Ele também demonstra o uso do Lazy<T>(Func<T>, Boolean) construtor (especificando true
para isThreadSafe
) e do Lazy<T>(Func<T>, LazyThreadSafetyMode) construtor (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ção usando os mesmos construtores, consulte o Lazy<T>(Func<T>) construtor.
O exemplo define uma classe LargeObject
que será inicializada lentamente 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 LargeObject
classe, que exibe uma mensagem quando o objeto é criado. No início do método Main
, o exemplo cria o inicializador thread-safe lento 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 passar antes da inicialização lenta ocorrer. Quando você pressiona a tecla Enter , o exemplo cria e inicia três threads. O ThreadProc
método usado pelos três threads chama a Value propriedade. Na primeira vez que isso acontecer, a LargeObject
instância 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 LargeObject
classe, 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
Observação
Para simplificar, este exemplo usa uma instância global de Lazy<T>, e todos os métodos são static
(Shared
no Visual Basic). Estes 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 Lazy<T> objeto que você cria especifica o tipo do objeto que você deseja inicializar preguiçosamente. O construtor que você usa para criar o Lazy<T> objeto determina as características da inicialização. A inicialização lenta ocorre na primeira vez 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 Lazy<T> objeto poderá criá-lo em qualquer thread. Você pode usar um dos construtores simples cujo comportamento padrão é criar um objeto thread-safe Lazy<T> , para que apenas uma instância do objeto preguiçosamente instanciado seja criada independentemente de quantos threads tentem acessá-lo. Para criar um Lazy<T> objeto 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 Lazy<T> objeto 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 seu 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 definido como isThreadSafe false . |
Lazy<T>(Func<T>, Boolean) com definido como isThreadSafe 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 todos os argumentos que você passa para o construtor do objeto inicializado preguiçosamente.
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 Value propriedade do Lazy<T> objeto, a mesma exceção será gerada em todas as tentativas subsequentes. Isso garante que cada chamada à Value propriedade produza o mesmo resultado e evite erros sutis que possam surgir se threads diferentes obtiverem resultados diferentes. O Lazy<T> valor significa um real T
que, caso contrário, teria sido inicializado 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 inicialização lenta.
Alternativa ao bloqueio Em determinadas situações, talvez você queira evitar a sobrecarga do comportamento de Lazy<T> bloqueio padrão do objeto. Em situações raras, pode haver um potencial para deadlocks. Nesses casos, você pode usar o construtor ou o Lazy<T>(LazyThreadSafetyMode) construtor Lazy<T>(Func<T>, LazyThreadSafetyMode) e especificar LazyThreadSafetyMode.PublicationOnly. Isso permite que o Lazy<T> objeto crie uma cópia do objeto inicializado preguiçosamente em cada um dos vários threads se os threads chamarem a Value propriedade simultaneamente. O Lazy<T> objeto 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. Assim, o custo de reduzir a sobrecarga de bloqueio é que o programa às vezes pode criar e descartar cópias extras de um objeto caro. Na maioria dos casos, isso é improvável. Os exemplos para os construtores e os Lazy<T>(LazyThreadSafetyMode) construtores Lazy<T>(Func<T>, LazyThreadSafetyMode) demonstram esse comportamento.
Importante
Quando você especifica LazyThreadSafetyMode.PublicationOnly, as exceções nunca são armazenadas em cache, mesmo que você especifique um método de fábrica.
Construtores equivalentes Além de habilitar o uso de LazyThreadSafetyMode.PublicationOnly, os construtores e Lazy<T>(Func<T>, LazyThreadSafetyMode) construtores Lazy<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 Lazy<T> objeto que seja | Para construtores que têm um LazyThreadSafetyMode mode parâmetro, 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 safe; 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 safe; threads correm para inicializar o valor. | PublicationOnly | Não aplicável. | Não aplicável. |
Outros recursos Para obter informações sobre o uso com Lazy<T> 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 a 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 a 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 a 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 do thread. |
Lazy<T>(LazyThreadSafetyMode) |
Inicializa uma nova instância da classe Lazy<T> que usa o construtor sem parâmetros |
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 de inicialização ociosa da instância 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 Object atual. (Herdado de Object) |
ToString() |
Cria e retorna uma representação de cadeia de caracteres da propriedade Value para esta instância. |
Aplica-se a
Acesso thread-safe
Por padrão, todos os membros públicos e protegidos da Lazy<T> classe 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.