Udostępnij za pośrednictwem


Tworzenie wstępnie obliczonych zadań

W tym artykule dowiesz się, jak używać Task.FromResult metody do pobierania wyników asynchronicznych operacji pobierania przechowywanych w pamięci podręcznej. Metoda FromResult zwraca gotowy Task<TResult> obiekt, który przechowuje podaną wartość jako jego Result właściwość. Ta metoda jest przydatna podczas wykonywania operacji asynchronicznej zwracającej Task<TResult> obiekt, a wynik tego Task<TResult> obiektu jest już obliczany.

Przykład

Poniższy przykład pobiera ciągi z Internetu. Definiuje metodę DownloadStringAsync . Ta metoda pobiera ciągi z sieci Web asynchronicznie. W tym przykładzie ConcurrentDictionary<TKey,TValue> użyto również obiektu do buforowania wyników poprzednich operacji. Jeśli adres wejściowy jest przechowywany w tej pamięci podręcznej, użyj FromResult metody , DownloadStringAsync aby utworzyć Task<TResult> obiekt, który przechowuje zawartość pod tym adresem. DownloadStringAsync W przeciwnym razie pobierze plik z Internetu i doda wynik do pamięci podręcznej.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;

public static class DownloadCache
{
    private static readonly ConcurrentDictionary<string, string> s_cachedDownloads = new();
    private static readonly HttpClient s_httpClient = new();

    public static Task<string> DownloadStringAsync(string address)
    {
        if (s_cachedDownloads.TryGetValue(address, out string? content))
        {
            return Task.FromResult(content);
        }

        return Task.Run(async () =>
        {
            content = await s_httpClient.GetStringAsync(address);
            s_cachedDownloads.TryAdd(address, content);

            return content;
        });
    }

    public static async Task Main()
    {
        string[] urls = new[]
        {
            "https://learn.microsoft.com/aspnet/core",
            "https://learn.microsoft.com/dotnet",
            "https://learn.microsoft.com/dotnet/architecture/dapr-for-net-developers",
            "https://learn.microsoft.com/dotnet/azure",
            "https://learn.microsoft.com/dotnet/desktop/wpf",
            "https://learn.microsoft.com/dotnet/devops/create-dotnet-github-action",
            "https://learn.microsoft.com/dotnet/machine-learning",
            "https://learn.microsoft.com/xamarin",
            "https://dotnet.microsoft.com/",
            "https://www.microsoft.com"
        };

        Stopwatch stopwatch = Stopwatch.StartNew();
        IEnumerable<Task<string>> downloads = urls.Select(DownloadStringAsync);

        static void StopAndLogElapsedTime(
            int attemptNumber, Stopwatch stopwatch, Task<string[]> downloadTasks)
        {
            stopwatch.Stop();

            int charCount = downloadTasks.Result.Sum(result => result.Length);
            long elapsedMs = stopwatch.ElapsedMilliseconds;

            Console.WriteLine(
                $"Attempt number: {attemptNumber}\n" +
                $"Retrieved characters: {charCount:#,0}\n" +
                $"Elapsed retrieval time: {elapsedMs:#,0} milliseconds.\n");
        }

        await Task.WhenAll(downloads).ContinueWith(
            downloadTasks => StopAndLogElapsedTime(1, stopwatch, downloadTasks));

        // Perform the same operation a second time. The time required
        // should be shorter because the results are held in the cache.
        stopwatch.Restart();

        downloads = urls.Select(DownloadStringAsync);

        await Task.WhenAll(downloads).ContinueWith(
            downloadTasks => StopAndLogElapsedTime(2, stopwatch, downloadTasks));
    }
    // Sample output:
    //     Attempt number: 1
    //     Retrieved characters: 754,585
    //     Elapsed retrieval time: 2,857 milliseconds.

    //     Attempt number: 2
    //     Retrieved characters: 754,585
    //     Elapsed retrieval time: 1 milliseconds.
}
Imports System.Collections.Concurrent
Imports System.Net.Http

Module Snippets
    Class DownloadCache
        Private Shared ReadOnly s_cachedDownloads As ConcurrentDictionary(Of String, String) =
            New ConcurrentDictionary(Of String, String)()
        Private Shared ReadOnly s_httpClient As HttpClient = New HttpClient()

        Public Function DownloadStringAsync(address As String) As Task(Of String)
            Dim content As String = Nothing

            If s_cachedDownloads.TryGetValue(address, content) Then
                Return Task.FromResult(Of String)(content)
            End If

            Return Task.Run(
                Async Function()
                    content = Await s_httpClient.GetStringAsync(address)
                    s_cachedDownloads.TryAdd(address, content)
                    Return content
                End Function)
        End Function
    End Class

    Public Sub StopAndLogElapsedTime(
            attemptNumber As Integer,
            stopwatch As Stopwatch,
            downloadTasks As Task(Of String()))

        stopwatch.Stop()

        Dim charCount As Integer = downloadTasks.Result.Sum(Function(result) result.Length)
        Dim elapsedMs As Long = stopwatch.ElapsedMilliseconds

        Console.WriteLine(
            $"Attempt number: {attemptNumber}{vbCrLf}" &
            $"Retrieved characters: {charCount:#,0}{vbCrLf}" &
            $"Elapsed retrieval time: {elapsedMs:#,0} milliseconds.{vbCrLf}")
    End Sub

    Sub Main()
        Dim cache As DownloadCache = New DownloadCache()
        Dim urls As String() = {
                "https://learn.microsoft.com/aspnet/core",
                "https://learn.microsoft.com/dotnet",
                "https://learn.microsoft.com/dotnet/architecture/dapr-for-net-developers",
                "https://learn.microsoft.com/dotnet/azure",
                "https://learn.microsoft.com/dotnet/desktop/wpf",
                "https://learn.microsoft.com/dotnet/devops/create-dotnet-github-action",
                "https://learn.microsoft.com/dotnet/machine-learning",
                "https://learn.microsoft.com/xamarin",
                "https://dotnet.microsoft.com/",
                "https://www.microsoft.com"
            }
        Dim stopwatch As Stopwatch = Stopwatch.StartNew()
        Dim downloads As IEnumerable(Of Task(Of String)) =
                urls.Select(AddressOf cache.DownloadStringAsync)
        Task.WhenAll(downloads).ContinueWith(
                Sub(downloadTasks)
                    StopAndLogElapsedTime(1, stopwatch, downloadTasks)
                End Sub).Wait()

        stopwatch.Restart()
        downloads = urls.Select(AddressOf cache.DownloadStringAsync)
        Task.WhenAll(downloads).ContinueWith(
                Sub(downloadTasks)
                    StopAndLogElapsedTime(2, stopwatch, downloadTasks)
                End Sub).Wait()
    End Sub
    ' Sample output:
    '     Attempt number 1
    '     Retrieved characters: 754,585
    '     Elapsed retrieval time: 2,857 milliseconds.
    '
    '     Attempt number 2
    '     Retrieved characters: 754,585
    '     Elapsed retrieval time: 1 milliseconds.
End Module

W poprzednim przykładzie po raz pierwszy po pobraniu każdego adresu URL jego wartość jest przechowywana w pamięci podręcznej. Metoda FromResult umożliwia metodzie DownloadStringAsync tworzenie Task<TResult> obiektów, które przechowują te wstępnie obliczone wyniki. Kolejne wywołania pobierania ciągu zwracają buforowane wartości i są znacznie szybsze.

Zobacz też