Önceden hesaplanan görevler oluşturma

Bu makalede, bir önbellekte tutulan zaman uyumsuz indirme işlemlerinin sonuçlarını almak için Task.FromResult yöntemini nasıl kullanacağınızı öğreneceksiniz. FromResult yöntemi, sağlanan değeri Result özelliği olarak tutan tamamlanmış bir Task<TResult> nesne döndürür. Bu yöntem, bir Task<TResult> nesnesi döndüren zaman uyumsuz bir işlem yaparken ve bu Task<TResult> nesnesinin sonucu önceden hesaplandığında oldukça kullanışlıdır.

Example

Aşağıdaki örnekte web'den dizeler indirilir. DownloadStringAsync yöntemini tanımlar. Bu yöntem web'den dizeleri zaman uyumsuz olarak indirir. Bu örnekte, önceki işlemlerin sonuçlarını önbelleğe almak için bir ConcurrentDictionary<TKey,TValue> nesnesi de kullanılır. Giriş adresi bu önbellekte tutulursa, DownloadStringAsync bu adreste FromResult içeriği tutan bir Task<TResult> nesne oluşturmak için yöntemini kullanır. Aksi takdirde, DownloadStringAsync dosyayı web'den indirir ve sonucu önbelleğe ekler.

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

Yukarıdaki örnekte, her url ilk kez indirildiğinde değeri önbellekte depolanır. FromResult yöntemi, DownloadStringAsync yönteminin bu önceden hesaplanan sonuçları tutan Task<TResult> nesneleri oluşturmasını sağlar. Dizeyi indirmek için yapılan sonraki çağrılar önbelleğe alınmış değerleri döndürür ve çok daha hızlıdır.

Ayrıca bakınız