首页 > 编程语言 >C# 中定义Task的扩展类

C# 中定义Task的扩展类

时间:2023-09-26 14:47:55浏览次数:30  
标签:Task return 定义 C# ActionResult new public string

目录

一 背景

在很多的时候我们代码中需要增加很多的Task对应的操作,这些操作很容易就分散在代码的各种地方从而造成大量的冗余,本篇文章主要是通过实际项目中用到的Task类进行一个归纳和总结,从而方便以后进行使用,我们代码中的返回值里面用到了一个称之为ActionResult的类作为返回的结果,所以在开始讲述TaskExtension之前会先来好好分析下这个ActionResult的用法,最后将会对整体上做一个总结。

二 代码分析

1. ActionResult

在很多场合下我们需要这样的一个类来返回我们执行某一个动作或者一系列动作的一个最终结果,通过这样一个sealed类我们就能够达到我们所需要的结果,比如执行一个动作是:成功、失败、超时、被拒绝、被取消、动作超时......这些,通过这样一个类就达到了封装整个动作执行的一个结果,如果自己的系统中有大量的是需要进行这类动作的执行,那么通过返回统一的ActionResult就能够统一所有的代码返回结果,这个是非常重要的,所以首先我们便需要来分析和理解这个过程,具体代码如下,可以进行具体的分析和使用。

public sealed class ActionResult
    {
        public bool IsSucceed { get; }
        public string Message { get; }
        public ResultType ResultType { get; }
        public bool HasError => ((ResultType & ResultType.Error) == ResultType.Error);
        public Dictionary<string, object> ResultData { get; }

        public ActionResult(bool result, string message, ResultType type) : this(result, message, type, null) { }

        public ActionResult(bool result, string message, ResultType type, Dictionary<string, object> data)
        {
            this.IsSucceed = result;
            this.Message = message;
            this.ResultType = type;
            this.ResultData = data;
        }

        /// <summary>
        /// Succeed status, ResultType is normal, message is empty.
        /// </summary>
        public static ActionResult Succeed => new ActionResult(true, string.Empty, ResultType.Normal);
        /// <summary>
        /// Succeed status, ResultType is Information, message is information.
        /// </summary>
        public static ActionResult SucceedWith(string information = "", Dictionary<string, object> data = null) => new ActionResult(true, information, ResultType.Information, data);
        /// <summary>
        /// Failed status, ResultType is error
        /// </summary>
        /// <param name="failMsg"> fail msg</param>
        public static ActionResult ErrorWith(string failMsg, Dictionary<string, object> data = null) => new ActionResult(false, failMsg, ResultType.Error, data);
        /// <summary>
        /// Failed status, ResultType is RepeatAction, message is empty.
        /// </summary>
        public static ActionResult RepeatAction => new ActionResult(false, string.Empty, ResultType.RepeatAction);
        /// <summary>
        /// Failed status, ResultType is RepeatAction, message is fail reason.
        /// </summary>
        public static ActionResult RepeatWith(string failMsg) => new ActionResult(false, failMsg, ResultType.RepeatAction);

        /// <summary>
        /// Failed status, indicate the action was rejected by remote, Result type is Reject, message is empty.
        /// </summary>
        public static ActionResult Reject => new ActionResult(false, string.Empty, ResultType.Reject);

        /// <summary>
        /// Failed status, indicate the action was rejected by remote, Result type is Reject.
        /// </summary>
        public static ActionResult RejectWith(string msg, Dictionary<string, object> data = null) => new ActionResult(false, msg, ResultType.Reject, data);
        /// <summary>
        /// Failed status, indicate the action was cancel by remote, Result type is Cancel.
        /// </summary>
        public static ActionResult CancelWith(string msg, Dictionary<string, object> data = null) => new ActionResult(false, msg, ResultType.Cancel, data);
        /// <summary>
        /// Failed status, indicate the action was timeout , Result type is timeout.
        /// </summary>
        public static ActionResult TimeOutWith(string msg, Dictionary<string, object> data = null) => new ActionResult(false, msg, ResultType.TimeOut, data);
        public bool Is(ResultType targetType)
        {
            return (this.ResultType & targetType) == targetType;
        }
    }

    [Flags]
    public enum ResultType
    {
        Normal = 0x01,
        Information = 0x01 << 1,

        RepeatAction = 0x01 << 2,
        Error = 0x01 << 3,

        Reject = 0x01 << 4,
        Cancel = 0x01 << 5,
        TimeOut = 0x01 << 6,

        Succeed = Normal | Information,
        Failed = RepeatAction | Error | Reject | Cancel | TimeOut
    }

    public static class ActionResultLogExtension
    {
        public static ActionResult WithLog(this ActionResult ar, Component component, string optTag = "")
        {
            string arMsg = ar.Message;
            if (string.IsNullOrEmpty(optTag) == false)
            {
                arMsg = $"[{optTag}]-[{arMsg}]";
            }
            switch (ar.ResultType)
            {
                case ResultType.Normal: component.LogInfo($"Action succeed."); break;
                case ResultType.Information: component.LogInfo($"Action succeed : {arMsg}"); break;
                case ResultType.RepeatAction: component.LogInfo($"Repeat action : {arMsg}"); break;
                case ResultType.Error: component.LogError($"Action was failed : {arMsg}"); break;
                case ResultType.Reject: component.LogError($"Action was reject, the reason is {arMsg}"); break;
                case ResultType.TimeOut: component.LogError($"Action was timeout, the reason is {arMsg}"); break;
                case ResultType.Cancel: component.LogError($"Action was cancel, the reason is {arMsg}"); break;
                default:
                    component.LogError($"Unknown action result type : " + ar.ResultType);
                    break;
            }

            return ar;
        }

        public static ActionResult Continue(this ActionResult ar, Action<ActionResult> action)
        {
            if (action != null)
            {
                try
                {
                    action(ar);
                }
                catch (Exception ex)
                {
                    string msg = $" | Execute continue action failed : [{action.Method.Name}, {ex.Message}]";
                    return new ActionResult(ar.IsSucceed, ar.Message + msg, ar.ResultType, ar.ResultData);
                }
            }
            return ar;
        }
    }

2 Task的扩展类TaskExtension

这个部分将主要包括几个方面:

2.1 Task中创建相互关联的Cancellation

我们知道在很多的Task中都支持Cancellation,而且这些Cancellation是可以通过一个静态方法CancellationTokenSource.CreateLinkedTokenSource方法来创建多个互相关联的Cancellation,这些互相关联的Cancellation中只要有一个触发了,那个整个Task就会被Cancel掉,所以下面的代码主要是完成这样一个统一的过程。

public static CancellationTokenSource CreateTimeoutCancellationTokenSource(this int millisecondsDelay, params CancellationToken[] linkedTokens)
        {
            if (millisecondsDelay <= 0) throw new ArgumentOutOfRangeException($"{millisecondsDelay} must >= 0");
            CancellationTokenSource cts = new CancellationTokenSource(millisecondsDelay);
            if (linkedTokens == null || false == linkedTokens.Any())
            {
                return cts;
            }
            CancellationToken[] tokens = new CancellationToken[linkedTokens.Length + 1];
            linkedTokens.CopyTo(tokens, 0);
            tokens[linkedTokens.Length] = cts.Token;
            return CancellationTokenSource.CreateLinkedTokenSource(tokens);
        }
2.2 Task中等待特定的结果返回

这个部分主要是围绕如何等待一个Func的结果,在我们的代码中我们可以在一个while的循环中使用Task.Delay来完成等待,当然在我们的代码中可以传入CancellationToken随时进行等待的取消,这些等待的方法中需要注意,有些是一直进行等待,有些还会加一个等待结果的超时时间超时后会退出当前等待的结果,这个需要注意代码中使用了一个DeviceTimer,这个是用于计时使用的,在之前的文章中有过相关的记载。

2.3 Task中等待执行结果的完成

这段代码其实内部是通过Task.WaitAll来完成的,这个实现的结果其实和Task.Wait(cancellationToken)的效果是一致的,这里添加的目的是为了统一代码的调用方,并且不需要关注这个Task执行是否出错,外部只需要拿到对应的结果就可以了。

public static bool WaitTaskResult(this Task<ActionResult> arTask, string optSource, out string failMsg, CancellationToken cancellationToken = default)
        {
            failMsg = string.Empty;
            try
            {
                Task.WaitAll(new[] { arTask }, cancellationToken);
                if (true == arTask.IsCompleted)
                {
                    ActionResult arResult = arTask.Result;
                    failMsg = arResult.Message;
                    return true == arResult.IsSucceed;
                }
                failMsg = $"{optSource} was canceled.";
                return false;
            }
            catch (Exception ex)
            {
                Log.Write(LogCategory.Error, optSource, "Wait for result of 'Task<ActionResult>' was failed : " + ex.Message);
                failMsg = ex.Message;
                return false;
            }
        }
2.4 Task执行增加Timeout

两个TimeoutAfter方法分别代表是否有返回值,一个是普通方法,一个是泛型方法,通过Task.whenAny来确定到底哪个方法先完成从而决定最终执行结果,如果正常执行则正常返回,否则则抛出TimeoutException

2.5 其它Task中使用的一些小技巧

很多人对于这个方法的扩展很不理解,不知道是做什么用的,其实主要用于下面的一种场景,如果在同步代码中调用Task方法,如果当前代码没有async或者await标识,其实编译器会在这个方法下面加上波浪线进行提示,加上这个IgnoreWait以后,代码中的提示会消失,这个是使用Task时的一个小技巧。

[MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void IgnoreWait(this Task task)
        { }
2.5 完整的代码展示
public static class TaskExtension
    {
        public const string MSG_TIME_OUT = "Task has time out!";
        public const string MSG_CANCELLED_BY_EXTERNAL = "Task was cancelled by external.";
        public const string MSG_ERROR_CONDITION_NOT_NULL = "condition cannot be null.";
        public const string MSG_ERROR_TIME_PERIOD_NOT_0 = "The timeout period cannot be zero";

        public static async Task<ActionResult> WaitConditionMeetAsync(this Func<Task<bool>> condition, string optName, int millsecsPeriod, int millisecsTimeout)
        {
            if (condition is null) throw new ArgumentNullException("Condition can not be null during waiting operation.");

            Log.Write(LogCategory.Information, "WaitConditionMeetAsync", $"Start wait for condition meeting of {optName}, Timeout setting is {millisecsTimeout} ms.");
            CancellationTokenSource cts = new CancellationTokenSource();
            cts.CancelAfter(millisecsTimeout);
            try
            {
                int delayTime = millsecsPeriod;
                while (true)
                {
                    if (cts.Token.IsCancellationRequested)
                    {
                        return ActionResult.ErrorWith($"Execute {optName} operation failed : timeout ({millisecsTimeout} ms)");
                    }
                    if (await condition.Invoke())
                    {
                        return ActionResult.Succeed;
                    }
                    await Task.Delay(delayTime);
                }
            }
            catch (Exception ex)
            {
                return ActionResult.ErrorWith(ex.Message);
            }
        }

        public static CancellationTokenSource CreateTimeoutCancellationTokenSource(this double secondsDelay, params CancellationToken[] linkedTokens)
        {
            return CreateTimeoutCancellationTokenSource((int)(secondsDelay * 1000), linkedTokens);
        }

        public static CancellationTokenSource CreateTimeoutCancellationTokenSource(this int millisecondsDelay, params CancellationToken[] linkedTokens)
        {
            if (millisecondsDelay <= 0) throw new ArgumentOutOfRangeException($"{millisecondsDelay} must >= 0");
            CancellationTokenSource cts = new CancellationTokenSource(millisecondsDelay);
            if (linkedTokens == null || false == linkedTokens.Any())
            {
                return cts;
            }
            CancellationToken[] tokens = new CancellationToken[linkedTokens.Length + 1];
            linkedTokens.CopyTo(tokens, 0);
            tokens[linkedTokens.Length] = cts.Token;
            return CancellationTokenSource.CreateLinkedTokenSource(tokens);
        }

        public static async Task<ActionResult> WaitConditionMeetAsync(this Func<bool> condition, string optName, int millsecsPeriod, CancellationToken cancellationToken)
        {
            if (condition is null) throw new ArgumentNullException("Condition can not be null during waiting operation.");
            if (cancellationToken == CancellationToken.None) throw new ArgumentException(nameof(cancellationToken));

            Log.Write(LogCategory.Information, "WaitConditionMeetAsync", $"Start wait for condition meeting of {optName}.");
            try
            {
                while (false == condition.Invoke())
                {
                    await Task.Delay(millsecsPeriod, cancellationToken);
                }
                return ActionResult.Succeed;
            }
            catch (TaskCanceledException)
            {
                return ActionResult.CancelWith($"Execute {optName} was canceled or timeout.");
            }
            catch (Exception ex)
            {
                return ActionResult.ErrorWith(ex.Message);
            }
        }

        public static async Task<ActionResult> WaitConditionMeetAsync(this Func<bool> condition, string optName, int millsecsPeriod, int millisecsTimeout, CancellationToken cancellationToken = default)
        {
            if (condition is null) throw new ArgumentNullException("Condition can not be null during waiting operation.");

            Log.Write(LogCategory.Information, "WaitConditionMeetAsync", $"Start wait for condition meeting of {optName}, Timeout setting is {millisecsTimeout} ms.");

            DeviceTimer deviceTimer = new DeviceTimer();
            deviceTimer.Start(millisecsTimeout);
            try
            {
                int delayTime = millsecsPeriod;
                while (true)
                {
                    if (condition.Invoke())
                    {
                        return ActionResult.Succeed;
                    }

                    if (deviceTimer.IsTimeout())
                    {
                        return ActionResult.ErrorWith($"Execute {optName} operation failed : timeout ({millisecsTimeout} ms)");
                    }

                    if (cancellationToken != default && true == cancellationToken.IsCancellationRequested)
                    {
                        return ActionResult.CancelWith($"Execute {optName} was canceled.");
                    }

                    await Task.Delay(delayTime, cancellationToken);
                }
            }
            catch (TaskCanceledException)
            {
                return ActionResult.CancelWith($"Execute {optName} was canceled.");
            }
            catch (Exception ex)
            {
                return ActionResult.ErrorWith(ex.Message);
            }
        }

        public static bool WaitTaskResult(this Task<ActionResult> arTask, string optSource, out string failMsg, CancellationToken cancellationToken = default)
        {
            failMsg = string.Empty;
            try
            {
                Task.WaitAll(new[] { arTask }, cancellationToken);
                if (true == arTask.IsCompleted)
                {
                    ActionResult arResult = arTask.Result;
                    failMsg = arResult.Message;
                    return true == arResult.IsSucceed;
                }
                failMsg = $"{optSource} was canceled.";
                return false;
            }
            catch (Exception ex)
            {
                Log.Write(LogCategory.Error, optSource, "Wait for result of 'Task<ActionResult>' was failed : " + ex.Message);
                failMsg = ex.Message;
                return false;
            }
        }

        private struct Void
        { } // just because TaskCompletionSource class has not generic version

        private static async Task<TResult> WithCancellation<TResult>(this Task<TResult> originalTask, CancellationToken ct)
        {
            var cancelTask = new TaskCompletionSource<Void>();
            // once CancellationToken was cancelled, complete the task
            using (ct.Register(t => ((TaskCompletionSource<Void>)t).TrySetResult(new Void()), cancelTask))
            {
                Task any = await Task.WhenAny(originalTask, cancelTask.Task);
                if (any == cancelTask.Task) ct.ThrowIfCancellationRequested();
            }
            return await originalTask;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void IgnoreWait(this Task task)
        { }

        // 有返回值
        public static async Task<TResult> TimeoutAfter<TResult>(this Task<TResult> task, TimeSpan timeout)
        {
            using (var timeoutCancellationTokenSource = new CancellationTokenSource())
            {
                var completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token));
                if (completedTask == task)
                {
                    timeoutCancellationTokenSource.Cancel();
                    return await task;  // Very important in order to propagate exceptions
                }
                else
                {
                    throw new TimeoutException("The operation has timed out.");
                }
            }
        }

        // 无返回值
        public static async Task TimeoutAfter(this Task task, TimeSpan timeout)
        {
            using (var timeoutCancellationTokenSource = new CancellationTokenSource())
            {
                var completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token));
                if (completedTask == task)
                {
                    timeoutCancellationTokenSource.Cancel();
                    await task;  // Very important in order to propagate exceptions
                }
                else
                {
                    throw new TimeoutException("The operation has timed out.");
                }
            }
        }
    }

标签:Task,return,定义,C#,ActionResult,new,public,string
From: https://www.cnblogs.com/seekdream/p/17729951.html

相关文章

  • Mac M1 上体验 AI 作图保姆教程
    本文介绍如何在MacM1Pro上通过苹果CoreML来安装和运行StableDiffusion。HowtoInstallandRunStableDiffusiononYourM1MacbookProusingCoreML.苹果的开源工具包苹果开源了一个工具包CoreMLStableDiffusion,使用CoreML(苹果的机器学习模型)来实现AI作图,通过......
  • CPU占用率高怎么办?-华为
    一、查看CPU占用率displaycpu命令,查看CPU占用率。<HUAWEI>displaycpuCPUutilizationstatisticsat2017-12-0111:17:44945msSystemCPUUsingPercentage:12%CPUutilizationforfiveseconds:12%,oneminute:12%,fiveminutes:11%.MaxCPUUsage:......
  • 错误 [email protected]: Permission denied (publickey). fatal: Could not read from r
    场景:本地git已登录了账号,需要在github下载项目时提示[email protected]:Permissiondenied(publickey).fatal:Couldnotreadfromremoterepository.,解决方法是获取本地的密钥,在gitHub中添加即可,不需要有账号上的对应,不需考虑本地切换git账号问题. 1.判断错误情况:permissio......
  • service.getClass().getAnnotation(XXXAnnotation.class) 取值为Null
    springboot2.7java8问题在使用工厂模式封装service时,需要通过service的class获取其类型注解,但是有些工厂类可以取到annotation注解,有些取不到渠道注解:/***xxx渠道注解**/@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@int......
  • C++虚函数剖析-从二级指针角度
    tags:C++categories:C++写在前面一直说C++的多态,其实底层原理是虚函数支持,那么虚函数的底层原理呢,之前一直停留在表面,直到后来看了很多书籍/视频/博客文章,才有了一点深刻的理解,下面来具体看看如何通过C指针进行虚函数的调用,相当于对C指针的一个复习,同时也......
  • MacOS 使用 Asan 编译 C++报警告malloc: nano zone abandoned due to inability to re
    问题clang(llvm)编译c++程序,带内存问题检查工具选项-fsanitize=address-fsanitize=undefined之后出现:malloc:nanozoneabandonedduetoinabilitytoreservevmspace.解决vi~/.zshrc#加入:exportMallocNanoZone=0source~/.zshrc参考:ios-malloc:nanozonea......
  • centos7忘记root密码重置办法~超简单
    此重置linuxroot密码的教程是在centoslinux环境中进行的!不同版本的linux重置密码的方法是有一定的差的!一.在下面的页面按“e”编辑内核信息二.删除“rhgbquiet”三.添加“rd.breakenforcing=0”,然后按Ctrl+X进入交互页面四.在交互页面输入重新挂载文件系统1.......
  • Pc 部署 Openwrt
    准备:WinPEphysdiskwriteOpenWrt固件WinpeWinpe操作DiskGenius删除磁盘分区写入镜像到磁盘#打开cmd切换到u盘分区u:#将img镜像写入磁盘physdiskwrite-uopenwrt.imgwrt操作##查看默认ip地址ipadd##设置内网ipucisetnetwork.lan.ipaddr=192.168.0.254ucicommitnet......
  • 在Spring Boot API Gateway中实现Sticky Session
    文章目录小结问题在APIGateway中实现StickySession在同一个APIGateway中同时支持StickySession和RoundRobinLoadBalancer参考小结在Kubernetes微服务的云环境中,如何在SpringBootAPIGateway中实现StickySession,当服务请求被某一个服务器处理后,所有后续的请求都被转发到被......
  • Websocket集群解决方案以及实战(附图文源码)
    最近在项目中在做一个消息推送的功能,比如客户下单之后通知给给对应的客户发送系统通知,这种消息推送需要使用到全双工的websocket推送消息。所谓的全双工表示客户端和服务端都能向对方发送消息。不使用同样是全双工的http是因为http只能由客户端主动发起请求,服务接收后返回消息。web......