.NET并發(fā)編程-函數(shù)式組合器
有問題就會有答案
本系列學(xué)習(xí)在.NET中的并發(fā)并行編程模式,實戰(zhàn)技巧
最近兩周有點拖更,主要去弄了個大黑耗子。也出現(xiàn)了不少事情,才發(fā)現(xiàn)和生活相比,工作真的是最簡單快樂的事了。所以就繼續(xù)聊點開心的,把.NET并發(fā)編程啃完。

本小節(jié)介紹以函數(shù)式的風(fēng)格來管理異常,特別是異步操作。使用函數(shù)組合器常見復(fù)雜任務(wù),提高編寫并發(fā)計算和處理副作用的能力
1、錯誤處理
在命令式編程中一般直接使用try/catch錯誤處理,在函數(shù)式編程中操作失敗不會返回null,而是包裝成一個成功或失敗的結(jié)構(gòu)表達形式。
1.1 用于錯誤處理的函數(shù)組合器
前面也已經(jīng)嘗試了Retry和Otherwise用于在異常情況下的應(yīng)用邏輯的異步Task操作。主要是通過利用Task類型的Status和Exception屬性來提供錯誤處理。
Image image = await Retry(async () => await DownloadImageAsync("Bugghina001.jpg").Otherwise(async () => await DownloadImageAsync("Bugghina002.jpg")),5, TimeSpan.FromSeconds(2));1.2 Task<Option<T>>處理錯誤
Option類型強制了開發(fā)人員必須編寫邏輯來檢查值是否存在,從而減輕了null值和error值的許多問題。Option類型的結(jié)果要么是Some,要么是None。
static async Task<Option<Image>> DownloadOptionImage(string blobReference){try{var container = await Helpers.GetCloudBlobContainerAsync().ConfigureAwait(false);CloudBlockBlob blockBlob = container.GetBlockBlobReference(blobReference);using (var memStream = new MemoryStream()){await blockBlob.DownloadToStreamAsync(memStream).ConfigureAwait(false);return Some(Image.FromStream(memStream));}}catch (Exception){return None;}}
Option類型和Match高階函數(shù)組合使用
DownloadOptionImage("Bugghina001.jpg").Map(opt => opt.Match(some: image => image.Save("ImageFolder\Bugghina.jpg"),none:()=>Log("下載失敗")));
1.3 使用Result類型保留異常信息
上面使用Task<Option>所有的異常都返回None,沒有記錄詳細的異常信息。Result類型定義如下
public struct Result<T>{public T Ok { get; }public Exception Error { get; }public bool IsFailed { get => Error != null; }public bool IsOk => !IsFailed;public Result(T ok){Ok = ok;Error = default(Exception);}public Result(Exception error){Error = error;Ok = default(T);}public R Match<R>(Func<T, R> okMap, Func<Exception, R> failureMap)=> IsOk ? okMap(Ok) : failureMap(Error);public void Match(Action<T> okAction, Action<Exception> errorAction){if (IsOk) okAction(Ok); else errorAction(Error);}public static implicit operator Result<T>(T ok) => new Result<T>(ok); // #Dpublic static implicit operator Result<T>(Exception error) => new Result<T>(error); // #Dpublic static implicit operator Result<T>(Result.Ok<T> ok) => new Result<T>(ok.Value); //#Dpublic static implicit operator Result<T>(Result.Failure error)=> new Result<T>(error.Error);}public static class Result{public struct Ok<L>{internal L Value { get; }internal Ok(L value) { Value = value; }}public struct Failure{internal Exception Error { get; }internal Failure(Exception error) { Error = error; }}}public static class ResultExtensions{public static Result.Ok<T> Ok<T>(T value) => new Result.Ok<T>(value);public static Result.Failure Failure(Exception error) => new Result.Failure(error);public static async Task<Result<T>> TryCatch<T>(Func<Task<T>> func){try{return await func();}catch (Exception ex){return ex;}}public static Task<Result<T>> TryCatch<T>(Func<T> func) => TryCatch(() => Task.FromResult(func()));public static async Task<Result<R>> SelectMany<T, R>(this Task<Result<T>> resultTask, Func<T, Task<Result<R>>> func){Result<T> result = await resultTask.ConfigureAwait(false);if (result.IsFailed)return result.Error;return await func(result.Ok);}public static async Task<Result<R>> Select<T, R>(this Task<Result<T>> resultTask, Func<T, Task<R>> func){Result<T> result = await resultTask.ConfigureAwait(false);if (result.IsFailed)return result.Error;return await func(result.Ok).ConfigureAwait(false);}public static async Task<Result<R>> Match<T, R>(this Task<Result<T>> resultTask, Func<T, Task<R>> actionOk, Func<Exception, Task<R>> actionError){Result<T> result = await resultTask.ConfigureAwait(false);if (result.IsFailed)return await actionError(result.Error);return await actionOk(result.Ok);}public static async Task<Result<T>> ToResult<T>(this Task<Option<T>> optionTask)where T : class{Option<T> opt = await optionTask.ConfigureAwait(false);if (opt.IsSome()) return Ok(opt.Value);return new Exception();}public static async Task<Result<R>> OnSuccess<T, R>(this Task<Result<T>> resultTask, Func<T, Task<R>> func){Result<T> result = await resultTask.ConfigureAwait(false);if (result.IsFailed)return result.Error;return await func(result.Ok).ConfigureAwait(false);}public static async Task<Result<T>> OnFailure<T>(this Task<Result<T>> resultTask, Func<Task> func){Result<T> result = await resultTask.ConfigureAwait(false);if (result.IsFailed)await func().ConfigureAwait(false);return result;}public static async Task<Result<R>> Bind<T, R>(this Task<Result<T>> resultTask, Func<T, Task<Result<R>>> func){Result<T> result = await resultTask.ConfigureAwait(false);if (result.IsFailed)return result.Error;return await func(result.Ok).ConfigureAwait(false);}public static async Task<Result<R>> Map<T, R>(this Task<Result<T>> resultTask, Func<T, Task<R>> func){Result<T> result = await resultTask.ConfigureAwait(false);if (result.IsFailed)return result.Error;return await TryCatch(() => func(result.Ok));}public static async Task<Result<R>> Match<T, R>(this Task<Result<T>> resultTask, Func<T, R> actionOk, Func<Exception, R> actionError){Result<T> result = await resultTask.ConfigureAwait(false);if (result.IsFailed)return actionError(result.Error);return actionOk(result.Ok);}public static async Task<Result<T>> Ensure<T>(this Task<Result<T>> resultTask, Func<T, Task<bool>> predicate, string errorMessage){Result<T> result = await resultTask.ConfigureAwait(false);if (result.IsFailed || !await predicate(result.Ok).ConfigureAwait(false)) return result.Error;return result.Ok;}public static async Task<Result<T>> Tap<T>(this Task<Result<T>> resultTask, Func<T, Task> action){Result<T> result = await resultTask.ConfigureAwait(false);if (result.IsOk)await action(result.Ok).ConfigureAwait(false);
使用Result將使代碼看起來更易讀一些,任務(wù)邏輯內(nèi)部不需要人為的去封裝錯誤返回結(jié)果。直接使用Result相同處理。例如下面這樣
static Result<byte[]> ReadFile(string path){if(System.IO.File.Exists(path)){return System.IO.File.ReadAllBytes(path);}else{return new FileNotFoundException(path);}}
2、函數(shù)式組合器
TPL內(nèi)置的異步組合器。例如Task.Run,Task.WhenAll和Task.WhenAny等。這些內(nèi)置組合器可以很容易地擴展來實現(xiàn)有用的組合器以組合和構(gòu)建更復(fù)雜的基于任務(wù)的模式。
to be contiued!
下集:使用TPL Dataflow的并行工作流與代理編程?
有問題就會有答案
