zoukankan      html  css  js  c++  java
  • Concurrency in C# Cookbook 笔记

    Pausing for a Period of Time
    Problem:
    You need to (asynchronously) wait for a period of time. This can be useful when unit
    testing or implementing retry delays. This solution can also be useful for simple time‐
    outs.
    Solution:
    The Task type has a static method Delay that returns a task that completes after the
    specified time。

    This example defines a task that completes asynchronously, for use with unit testing.
    When faking an asynchronous operation, it’s important to test at least synchronous
    success and asynchronous success as well as asynchronous failure. This example returns
    a task used for the asynchronous success case:

    static async Task<T> DelayResult<T>(T result, TimeSpan delay)
    {
    await Task. Delay(delay);
    return result;
    }

    This next example is a simple implementation of an exponential backoff, that is, a retry
    strategy where you increase the delays between retries. Exponential backoff is a best
    practice when working with web services to ensure the server does not get flooded with
    retries.
    For production code, I would recommend a more thorough solu‐
    tion, such as the Transient Error Handling Block in Microsoft’s En‐
    terprise Library; the following code is just a simple example of
    Task.Delay usage.

    static async Task<string> DownloadStringWithRetries(string uri)
    {
     using (var client = new HttpClient())
     {
     // Retry after 1 second, then after 2 seconds, then 4.
    var nextDelay = TimeSpan. FromSeconds(1);
     for (int i = 0; i != 3; ++i)
     {
     try
     {
     return await client. GetStringAsync(uri);
     }
     catch
     {
     }
     await Task. Delay(nextDelay);
     nextDelay = nextDelay + nextDelay;
     }
     // Try one last time, allowing the error to propogate.
     return await client. GetStringAsync(uri);
     }
    }

    This final example uses Task.Delay as a simple timeout; in this case, the desired se‐
    mantics are to return null if the service does not respond within three seconds:

    static async Task<string> DownloadStringWithTimeout(string uri)
    {
    using (var client = new HttpClient())
    {
    var downloadTask = client. GetStringAsync(uri);
    var timeoutTask = Task. Delay(3000);
    var completedTask = await Task. WhenAny(downloadTask, timeoutTask);
    if (completedTask == timeoutTask)
    return null;
    return await downloadTask;
    }
    }


    Discussion
    Task.Delay is a fine option for unit testing asynchronous code or for implementing
    retry logic. However, if you need to implement a timeout, a CancellationToken is usu‐
    ally a better choice.
    See Also
    Recipe 2.5 covers how Task.WhenAny is used to determine which task completes first.
    Recipe 9.3 covers using CancellationToken as a timeout

    2.2. Returning Completed Tasks
    Problem
    You need to implement a synchronous method with an asynchronous signature. This
    situation can arise if you are inheriting from an asynchronous interface or base class
    but wish to implement it synchronously. This technique is particularly useful when unit
    testing asynchronous code, when you need a simple stub or mock for an asynchronous
    interface.
    Solution
    You can use Task.FromResult to create and return a new Task<T> that is already com‐
    pleted with the specified value

    interface IMyAsyncInterface
    {
     Task<int> GetValueAsync();
    }
    class MySynchronousImplementation : IMyAsyncInterface
    {
     public Task<int> GetValueAsync()
     {
     return Task. FromResult(13);
     }
    }

    2.3. Reporting Progress
    Problem
    You need to respond to progress while an asynchronous operation is executing.
    Solution
    Use the provided IProgress<T> and Progress<T> types. Your async method should
    take an IProgress<T> argument; the T is whatever type of progress you need to report

    static async Task MyMethodAsync(IProgress<double> progress = null)
    {
     double percentComplete = 0;
     while (! done)
     {
     ...
     if (progress != null)
     progress. Report(percentComplete);
     }
    }

    Calling code can use it as such:

    static async Task CallMyMethodAsync()
    {
     var progress = new Progress<double>();
     progress. ProgressChanged += (sender, args) =>
     {
     ...
     };
     await MyMethodAsync(progress);
    }

    By convention, the IProgress<T> parameter may be null if the caller does not need
    progress reports, so be sure to check for this in your async method.
    Bear in mind that the IProgress<T>.Report method may be asynchronous. This means
    that MyMethodAsync may continue executing before the progress is actually reported.
    For this reason, it’s best to define T as an immutable type or at least a value type. If T is
    a mutable reference type, then you’ll have to create a separate copy yourself each time
    you call IProgress<T>.Report.
    Progress<T> will capture the current context when it is constructed and will invoke its
    callback within that context. This means that if you construct the Progress<T> on the
    UI thread, then you can update the UI from its callback, even if the asynchronous
    method is invoking Report from a background thread.
    When a method supports progress reporting, it should also make a best effort to support
    cancellation

    If all the tasks have the same result type and they all complete successfully, then the
    Task.WhenAll task will return an array containing all the task results:

    Task task1 = Task. FromResult(3);
    Task task2 = Task. FromResult(5);
    Task task3 = Task. FromResult(7);
    int[] results = await Task. WhenAll(task1, task2, task3);
    // "results" contains { 3, 5, 7 }

     There is an overload of Task.WhenAll that takes an IEnumerable of tasks; however, I
    do not recommend that you use it. Whenever I mix asynchronous code with LINQ, I
    find the code is clearer when I explicitly “reify” the sequence (i.e., evaluate the sequence,
    creating a collection):

    static async Task<string> DownloadAllAsync(IEnumerable<string> urls)
    {
     var httpClient = new HttpClient();
     // Define what we're going to do for each URL.
     var downloads = urls. Select(url => httpClient. GetStringAsync(url));
     // Note that no tasks have actually started yet
     // because the sequence is not evaluated.
     // Start all URLs downloading simultaneously.
     Task<string>[] downloadTasks = downloads. ToArray();
     // Now the tasks have all started.
     // Asynchronously wait for all downloads to complete.
     string[] htmlPages = await Task. WhenAll(downloadTasks);
     return string. Concat(htmlPages);
    }
    // Returns the length of data at the first URL to respond.
    private static async Task<int> FirstRespondingUrlAsync(string urlA, string urlB)
    {
     var httpClient = new HttpClient();
     // Start both downloads concurrently.
     Task<byte[]> downloadTaskA = httpClient. GetByteArrayAsync(urlA);
     Task<byte[]> downloadTaskB = httpClient. GetByteArrayAsync(urlB);
     // Wait for either of the tasks to complete.
     Task<byte[]> completedTask =
     await Task. WhenAny(downloadTaskA, downloadTaskB);
     // Return the length of the data retrieved from that URL.
     byte[] data = await completedTask;
     return data. Length;
    }
    static async Task<int> DelayAndReturnAsync(int val)
    {
     await Task. Delay(TimeSpan. FromSeconds(val));
     return val;
    }
    static async Task AwaitAndProcessAsync(Task<int> task)
    {
     var result = await task;
     Trace. WriteLine(result);
    }
    // This method now prints "1", "2", and "3".
    static async Task ProcessTasksAsync()
    {
     // Create a sequence of tasks.
     Task<int> taskA = DelayAndReturnAsync(2);
     Task<int> taskB = DelayAndReturnAsync(3);
     Task<int> taskC = DelayAndReturnAsync(1);
     var tasks = new[] { taskA, taskB, taskC };
     var processingTasks = (from t in tasks
     select AwaitAndProcessAsync(t)). ToArray();
     // Await all processing to complete
     await Task. WhenAll(processingTasks);
    }

    Alternatively, this can be written as:

    static async Task<int> DelayAndReturnAsync(int val)
    {
     await Task. Delay(TimeSpan. FromSeconds(val));
     return val;
    }
    // This method now prints "1", "2", and "3".
    static async Task ProcessTasksAsync()
    {
     // Create a sequence of tasks.
     Task<int> taskA = DelayAndReturnAsync(2);
     Task<int> taskB = DelayAndReturnAsync(3);
     Task<int> taskC = DelayAndReturnAsync(1);
     var tasks = new[] { taskA, taskB, taskC };
     var processingTasks = tasks. Select(async t =>
     {
     var result = await t;
     Trace. WriteLine(result);
     }). ToArray();
     // Await all processing to complete
     await Task. WhenAll(processingTasks);
    }
  • 相关阅读:
    IIS:日志代码分析
    SQL:查找被锁的表,以及锁表的SQL语句(重点推荐)
    SQL 2000/2005/2008 收缩日志方法
    SQL SERVER:使用工具观察与分析数据库中锁信息
    C# : Post 接收或发送XML
    WCF:没有终结点在侦听可以接受消息的*这通常是由于不正确的地址或者 SOAP操作导致的。
    SQL2005 遍历表插入
    SQL2005:SQL Server 2005还原数据库时出现“不能选择文件或文件组XXX_log用于此操作的解决办法
    C#:安装Windows服务,动态指定服务名及描述
    IE6与 javascript:void(0)
  • 原文地址:https://www.cnblogs.com/ly7454/p/4340240.html
Copyright © 2011-2022 走看看