从状态机的角度async和await的实现原理

news2024/11/18 5:42:08

一. 深度剖析

准备:

  先给VS安装一个插件ILSpy,这样更容易反编译代码进行查看,另外要注意反编译async和await的时候,要把C#代码版本改为4.0哦。

1.什么是状态机

 (1).含义:通常我们所说的状态机(State Machine)指的是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型,可以理解成一个状态转换图。(状态机是计算机科学的重要基础概念之一,也可以说是一种总结归纳问题的思想,应用范围非常广泛)

 (2).例子:自动门有两个状态,open 和 closed ,closed 状态下,如果读取开门信号,那么状态就会切换为 open 。open 状态下如果读取关门信号,状态就会切换为 closed .

 (3).涉及到4个相关概念:

  A.状态(State):一个状态机至少包括两个状态.(例如上面自动门的例子,有 open 和 closed 两个状态。)

  B.事件(Event):事件就是执行某个操作的触发条件或者口令.(对于自动门,“按下开门按钮”就是一个事件。)

  C.动作(Action):事件发生以后要执行的动作,一个action对应一个函数.(事件是“按开门按钮”,动作是“开门”)

  D.变换(Transition):从一个状态转换成另外一个状态.(“开门过程”就是一个变换。)

 (4). C#的状态机提供了IAsyncStateMachine接口,里面有MoveNext 和 SetStateMachine方法处理相应业务.

2. 状态机分析

  async关键字标记方法是一个异步方法,编译器通过这个标记去改造这个方法体为创建状态机的方法。await是关键字是为了实现状态机中的一个状态, 每当有一个await,就会生成一个对应的状态。状态机就是根据这个状态,去一步步的调用异步委托,然后回调,包括状态机的解析。

(1).状态机的默认状态都是-1, 结束状态都是-2.

(2).每await一次就会产生一个 TaskAwaiter<int> awaiter; 改变状态机的状态, 当有多个await的时候,每个await都会改变状态机的状态,比如 改为 0,1,2,3,4 等等, 分别表示 代码中await xxx 这句话执行完成。

(3).状态机的执行套路:

  A. 首先创建一个 <xxx>d_num 的方法, xxx代表方法名,num可能是0,1,2,3等,实现IAsyncStateMachine接口。

  B. 在MoveNext方法中, 源代码中每个 await xxxx 都会对应生成是一个 TaskAwaiter<int> awaiter,然后 xxxx.GetAwaiter()

  C. 判断状态机是否执行完if (!awaiter.IsCompleted),没有执行完的话走 <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); 代表释放当前线程

  D. 执行完后走,<>s__1 = awaiter.GetResult(); 拿到返回值,继续走后面的代码。

(此处写的比较抽象,看下面3 结合代码编译再分析)

3. 结合代码编译分析

前提:准备1个Index方法,我们把它当做主方法,在该方法里面调用 F1Async-F5Async这五个方法. (要补充截图这里)

代码分享:

 public class Home2Controller : Controller
    {

        /// <summary>
        /// 该方法为主方法,用于调用下面的F1-F5的方法
        /// </summary>
        /// <returns></returns>
        public async Task<IActionResult> Index()
        {
            await F1Async();
            await F2Async();
            await F3Async();
            await F4Async();
            await F5Async();

            return View();
        }

        /// <summary>
        /// 没有加async和await的方法
        /// (也是一个计算密集型的异步方法,只是编译的时候本身不会被编译成状态机)
        /// </summary>
        /// <returns></returns>
        public static Task<int> F1Async()
        {
            return Task.Run(() =>
            {
                return 2;
            });
        }

        /// <summary>
        /// 只要标记了async 就会被编译成状态机
        /// 如果方法声明为 async,那么可以直接 return 具体的值,不再用创建Task,由编译器创建 Task: 
        /// </summary>
        /// <returns></returns>
        public static async Task<int> F2Async()
        {
            return 2;
        }

        /// <summary>
        /// 计算密集型的异步方法
        /// (方法本身也会被编译成状态机)
        /// </summary>
        /// <returns></returns>
        public static async Task<int> F3Async()
        {
            return await Task.Run(() =>
            {
                return 2;
            });
        }

        /// <summary>
        /// I/O密集型的异步方法
        /// </summary>
        /// <returns></returns>
        public async Task<int> F4Async()
        {
            AsyncDBContext context = new AsyncDBContext();
            for (int i = 0; i < 10000; i++)
            {
                UserInfor uInfor = new UserInfor()
                {
                    id = Guid.NewGuid().ToString("N"),
                    userName = "ypf",
                    addTime = DateTime.Now
                };
                await context.AddAsync(uInfor);
            }
            return await context.SaveChangesAsync();
        }


      /// <summary>
      /// 没有创建状态机,但是new 了1个新的 task
      /// </summary>
      /// <returns></returns>
       public static Task<int> F5Async()
        {
            //内部是new Task<TResult>(result)
            return Task.FromResult(3);
        }

    }

 (1).F1Async:没有加async和await,但它本身也是一个计算密集型的异步方法,该方法本身不会被编译成状态机,但调用它的方法Index会被编译成状态机。

 (2).F2Async:只加了async,会生成状态机,但由于没有加await所以不会涉及到中间状态的变化,从-1默认状态 变为 结束的-2状态。

更多C++后台开发技术点知识内容包括C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,MongoDB,ZK,流媒体,音视频开发,Linux内核,TCP/IP,协程,DPDK多个高级知识点。

C/C++Linux服务器开发高级架构师/C++后台开发架构师免费学习地址

【文章福利】另外还整理一些C++后台开发架构师 相关学习资料,面试题,教学视频,以及学习路线图,免费分享有需要的可以点击领取

代码分享:

 1   public class Home2Controller : Controller
 2     {
 3 
 4         /// <summary>
 5         /// 该方法为主方法,用于调用下面的F1-F5的方法
 6         /// </summary>
 7         /// <returns></returns>
 8         public async Task<IActionResult> Index()
 9         {
10             await F1Async();
11             await F2Async();
12             await F3Async();
13             await F4Async();
14             await F5Async();
15 
16             return View();
17         }
18 
19         /// <summary>
20         /// 没有加async和await的方法
21         /// (也是一个计算密集型的异步方法,只是编译的时候本身不会被编译成状态机)
22         /// </summary>
23         /// <returns></returns>
24         public static Task<int> F1Async()
25         {
26             return Task.Run(() =>
27             {
28                 return 2;
29             });
30         }
31 
32         /// <summary>
33         /// 只要标记了async 就会被编译成状态机
34         /// 如果方法声明为 async,那么可以直接 return 具体的值
35         /// <returns></returns>
36         public static async Task<int> F2Async()
37         {
38             return 2;
39         }
40 
41         /// <summary>
42         /// 计算密集型的异步方法
43         /// (方法本身也会被编译成状态机)
44         /// </summary>
45         /// <returns></returns>
46         public static async Task<int> F3Async()
47         {
48             return await Task.Run(() =>
49             {
50                 return 2;
51             });
52         }
53 
54         /// <summary>
55         /// I/O密集型的异步方法
56         /// </summary>
57         /// <returns></returns>
58         public async Task<int> F4Async()
59         {
60             AsyncDBContext context = new AsyncDBContext();
61             for (int i = 0; i < 10000; i++)
62             {
63                 UserInfor uInfor = new UserInfor()
64                 {
65                     id = Guid.NewGuid().ToString("N"),
66                     userName = "ypf",
67                     addTime = DateTime.Now
68                 };
69                 await context.AddAsync(uInfor);
70             }
71             return await context.SaveChangesAsync();
72         }
73 
74 
75       /// <summary>
76       /// 没有创建状态机,但是new 了1个新的 task
77       /// </summary>
78       /// <returns></returns>
79        public static Task<int> F5Async()
80         {
81             //内部是new Task<TResult>(result)
82             return Task.FromResult(3);
83         }
84 
85     }

核心代码剖析:

 (3).F3Async:既有async也有await (await只有1个),该方法是使用了Task.Run,我们把它归为计算型的异步方法。

代码分享:

       [AsyncStateMachine(typeof(<F3Async>d__3))]
    [DebuggerStepThrough]
    public static Task<int> F3Async()
    {
        <F3Async>d__3 stateMachine = new <F3Async>d__3();
        stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create();
        stateMachine.<>1__state = -1;
        AsyncTaskMethodBuilder<int> <>t__builder = stateMachine.<>t__builder;
        <>t__builder.Start(ref stateMachine);
        return stateMachine.<>t__builder.Task;
    }
        [CompilerGenerated]
    private sealed class <F3Async>d__3 : IAsyncStateMachine
    {
        public int <>1__state;

        public AsyncTaskMethodBuilder<int> <>t__builder;

        private int <>s__1;

        private TaskAwaiter<int> <>u__1;

        private void MoveNext()
        {
            int num = <>1__state;
            int result;
            try
            {
                TaskAwaiter<int> awaiter;
                if (num != 0)
                {
                    awaiter = Task.Run(() => 2).GetAwaiter();
                    if (!awaiter.IsCompleted)
                    {
                        num = (<>1__state = 0);
                        <>u__1 = awaiter;
                        <F3Async>d__3 stateMachine = this;
                        <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
                        return;
                    }
                }
                else
                {
                    awaiter = <>u__1;
                    <>u__1 = default(TaskAwaiter<int>);
                    num = (<>1__state = -1);
                }
                <>s__1 = awaiter.GetResult();
                result = <>s__1;
            }
            catch (Exception exception)
            {
                <>1__state = -2;
                <>t__builder.SetException(exception);
                return;
            }
            <>1__state = -2;
            <>t__builder.SetResult(result);
        }

        void IAsyncStateMachine.MoveNext()
        {
            //ILSpy generated this explicit interface implementation from .override directive in MoveNext
            this.MoveNext();
        }

        [DebuggerHidden]
        private void SetStateMachine(IAsyncStateMachine stateMachine)
        {
        }

        void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
        {
            //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine
            this.SetStateMachine(stateMachine);
        }
    }

核心代码剖析:

(4).F4Async:既有async又有await,且两个await,两个await按照顺序执行。

代码分享:

        [AsyncStateMachine(typeof(<F4Async>d__4))]
    [DebuggerStepThrough]
    public Task<int> F4Async()
    {
        <F4Async>d__4 stateMachine = new <F4Async>d__4();
        stateMachine.<>4__this = this;
        stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create();
        stateMachine.<>1__state = -1;
        AsyncTaskMethodBuilder<int> <>t__builder = stateMachine.<>t__builder;
        <>t__builder.Start<<F4Async>d__4>(ref stateMachine);
        return stateMachine.<>t__builder.get_Task();
    }
[CompilerGenerated]
    private sealed class <F4Async>d__4 : IAsyncStateMachine
    {
        public int <>1__state;

        public AsyncTaskMethodBuilder<int> <>t__builder;

        public Home2Controller <>4__this;

        private AsyncDBContext <context>5__1;

        private int <i>5__2;

        private UserInfor <uInfor>5__3;

        private int <>s__4;

        private ValueTaskAwaiter<EntityEntry<UserInfor>> <>u__1;

        private TaskAwaiter<int> <>u__2;

        private void MoveNext()
        {
        
            int num = <>1__state;
            int result;
            try
            {
                ValueTaskAwaiter<EntityEntry<UserInfor>> awaiter;
                if (num == 0)
                {
                    awaiter = <>u__1;
                    <>u__1 = default(ValueTaskAwaiter<EntityEntry<UserInfor>>);
                    num = (<>1__state = -1);
                    goto IL_00e8;
                }
                if (num != 1)
                {
                    <context>5__1 = new AsyncDBContext();
                    <i>5__2 = 0;
                    goto IL_010a;
                }
                TaskAwaiter<int> awaiter2 = <>u__2;
                <>u__2 = default(TaskAwaiter<int>);
                num = (<>1__state = -1);
                goto IL_0188;
                IL_00e8:
                awaiter.GetResult();
                <uInfor>5__3 = null;
                <i>5__2++;
                goto IL_010a;
                IL_010a:
                if (<i>5__2 < 10000)
                {
                    <uInfor>5__3 = new UserInfor
                    {
                        id = Guid.NewGuid().ToString("N"),
                        userName = "ypf",
                        addTime = DateTime.Now
                    };
                    awaiter = ((DbContext)<context>5__1).AddAsync<UserInfor>(<uInfor>5__3, default(CancellationToken)).GetAwaiter();
                    if (!awaiter.get_IsCompleted())
                    {
                        num = (<>1__state = 0);
                        <>u__1 = awaiter;
                        <F4Async>d__4 stateMachine = this;
                        <>t__builder.AwaitUnsafeOnCompleted<ValueTaskAwaiter<EntityEntry<UserInfor>>, <F4Async>d__4>(ref awaiter, ref stateMachine);
                        return;
                    }
                    goto IL_00e8;
                }
                awaiter2 = ((DbContext)<context>5__1).SaveChangesAsync(default(CancellationToken)).GetAwaiter();
                if (!awaiter2.get_IsCompleted())
                {
                    num = (<>1__state = 1);
                    <>u__2 = awaiter2;
                    <F4Async>d__4 stateMachine = this;
                    <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <F4Async>d__4>(ref awaiter2, ref stateMachine);
                    return;
                }
                goto IL_0188;
                IL_0188:
                <>s__4 = awaiter2.GetResult();
                result = <>s__4;
            }
            catch (Exception exception)
            {
                <>1__state = -2;
                <context>5__1 = null;
                <>t__builder.SetException(exception);
                return;
            }
            <>1__state = -2;
            <context>5__1 = null;
            <>t__builder.SetResult(result);
        }

        [DebuggerHidden]
        private void SetStateMachine(IAsyncStateMachine stateMachine)
        {
        }
    }

核心代码剖析:

 (5).F5Async:没有async和await,没有创建状态机,但是new 了1个新的 task。

 (6).Index:多个await,通过goto 一步一步跳转,按顺序执行。

代码分享:

[CompilerGenerated]
    private sealed class <Index>d__0 : IAsyncStateMachine
    {
        public int <>1__state;

        public AsyncTaskMethodBuilder<IActionResult> <>t__builder;

        public Home2Controller <>4__this;

        private TaskAwaiter<int> <>u__1;

        private void MoveNext()
        {
            
            int num = <>1__state;
            IActionResult result;
            try
            {
                TaskAwaiter<int> awaiter5;
                TaskAwaiter<int> awaiter4;
                TaskAwaiter<int> awaiter3;
                TaskAwaiter<int> awaiter2;
                TaskAwaiter<int> awaiter;
                switch (num)
                {
                default:
                    awaiter5 = F1Async().GetAwaiter();
                    if (!awaiter5.get_IsCompleted())
                    {
                        num = (<>1__state = 0);
                        <>u__1 = awaiter5;
                        <Index>d__0 stateMachine = this;
                        <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <Index>d__0>(ref awaiter5, ref stateMachine);
                        return;
                    }
                    goto IL_0091;
                case 0:
                    awaiter5 = <>u__1;
                    <>u__1 = default(TaskAwaiter<int>);
                    num = (<>1__state = -1);
                    goto IL_0091;
                case 1:
                    awaiter4 = <>u__1;
                    <>u__1 = default(TaskAwaiter<int>);
                    num = (<>1__state = -1);
                    goto IL_00f3;
                case 2:
                    awaiter3 = <>u__1;
                    <>u__1 = default(TaskAwaiter<int>);
                    num = (<>1__state = -1);
                    goto IL_0155;
                case 3:
                    awaiter2 = <>u__1;
                    <>u__1 = default(TaskAwaiter<int>);
                    num = (<>1__state = -1);
                    goto IL_01bd;
                case 4:
                    {
                        awaiter = <>u__1;
                        <>u__1 = default(TaskAwaiter<int>);
                        num = (<>1__state = -1);
                        break;
                    }
                    IL_01bd:
                    awaiter2.GetResult();
                    awaiter = F5Async().GetAwaiter();
                    if (!awaiter.get_IsCompleted())
                    {
                        num = (<>1__state = 4);
                        <>u__1 = awaiter;
                        <Index>d__0 stateMachine = this;
                        <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <Index>d__0>(ref awaiter, ref stateMachine);
                        return;
                    }
                    break;
                    IL_0091:
                    awaiter5.GetResult();
                    awaiter4 = F2Async().GetAwaiter();
                    if (!awaiter4.get_IsCompleted())
                    {
                        num = (<>1__state = 1);
                        <>u__1 = awaiter4;
                        <Index>d__0 stateMachine = this;
                        <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <Index>d__0>(ref awaiter4, ref stateMachine);
                        return;
                    }
                    goto IL_00f3;
                    IL_0155:
                    awaiter3.GetResult();
                    awaiter2 = <>4__this.F4Async().GetAwaiter();
                    if (!awaiter2.get_IsCompleted())
                    {
                        num = (<>1__state = 3);
                        <>u__1 = awaiter2;
                        <Index>d__0 stateMachine = this;
                        <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <Index>d__0>(ref awaiter2, ref stateMachine);
                        return;
                    }
                    goto IL_01bd;
                    IL_00f3:
                    awaiter4.GetResult();
                    awaiter3 = F3Async().GetAwaiter();
                    if (!awaiter3.get_IsCompleted())
                    {
                        num = (<>1__state = 2);
                        <>u__1 = awaiter3;
                        <Index>d__0 stateMachine = this;
                        <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <Index>d__0>(ref awaiter3, ref stateMachine);
                        return;
                    }
                    goto IL_0155;
                }
                awaiter.GetResult();
                result = <>4__this.View();
            }
            catch (Exception exception)
            {
                <>1__state = -2;
                <>t__builder.SetException(exception);
                return;
            }
            <>1__state = -2;
            <>t__builder.SetResult(result);
        }

        [DebuggerHidden]
        private void SetStateMachine(IAsyncStateMachine stateMachine)
        {
        }
    }

核心代码剖析:

4. 重点比较一下:F1Async 和 F3Async 的区别

 (1).F1Async和F3Async都是异步方法,在外层Index方法中调用的时候,都要加await,就外层而言都不会卡主线程,外层方法都会被编译成状态机。

 (2).从编译的角度而言 F1Async 方法本身不会被编译成状态机,F3Async方法本身会被编译成状态机。

5. 再次扩展

 (1).等待的时候要用 await xxxAsync, 而不要用 xxxAsync.wait() 和 .Result

 (2).等待多个用 await Task.WhenAll 而不要用 Task.WaitAll

原因?

 后者是同步写法啊,阻塞线程的,从上面的编译的源码可以看出来,没有await不会生成TaskAwaiter<int> awaiter。

二. 几个用法

1. 异常捕获

代码1

public static async void EmailAsync() {
    List<string> addrs = new List<string>();
    IEnumerable<Task> asyncOps = addrs.Select(addr => SendMailAsync(addr));
    try {
        await Task.WhenAll(asyncOps);
    } catch (AggregateException ex) {
        // 可以通过 InnerExceptions 来得到内部返回的异常
        var exceptions = ex.InnerExceptions;
        // 也可以使用 Handle 对每个异常进行处理
        ex.Handle(innerEx => {
            // 此处的演示仅仅为了说明 ex.Handle 可以对异常进行单独处理
            // 实际项目中不一定会抛出此异常

            if (innerEx is OperationCanceledException oce) {
                // 对 OperationCanceledException 进行单独的处理
                return true;
            } else if (innerEx is UnauthorizedAccessException uae) {
                // 对 UnauthorizedAccessException 进行单独处理
                return true;
            }
            return false;
        });
    }
}

代码2

public static async void EmailAsync() {
    List<string> addrs = new List<string>();
    IEnumerable<Task> asyncOps = addrs.Select(addr => SendMailAsync(addr));
    try {
        await Task.WhenAll(asyncOps);
    } catch (AggregateException ex) {
        // 此处可以针对每个任务进行更加具体的管理
        foreach (Task<string> task in asyncOps) {
            if (task.IsCanceled) {
            }else if (task.IsFaulted) {
            }else if (task.IsCompleted) {
            }
        }
    }
}

代码3

try
{
    HttpClient hc = new HttpClient();
    var task1 = hc.GetStringAsync(textBox1.Text);
    var task2 = hc.GetStringAsync(textBox2.Text);
    var task3 = hc.GetStringAsync(textBox3.Text);
    Task.WaitAll(task1, task2, task3);
    label1.Text = task1.Result.Length.ToString();
    label2.Text = task2.Result.Length.ToString();
    label3.Text = task3.Result.Length.ToString();
}
catch (AggregateException ae)
{
    MessageBox.Show(ae.GetBaseException().ToString());
}

原文链接:第十七节:从状态机的角度async和await的实现原理(新) - Yaopengfei - 博客园

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/160149.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

6.5、文件传送协议FTP

将某台计算机中的文件通过网络传送到可能相距很远的另一台计算机中&#xff0c;是一项基本的网络应用&#xff0c;即文件传送。 文件传送协议FTP\color{red}文件传送协议\texttt{FTP}文件传送协议FTP (File Transfer Protocol)是因特网上使用得最广泛的文件传送协议。 FTP 提供…

记录--Vue开发历程---音乐播放器

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 一、audio标签的使用 1、Audio 对象属性 2、对象方法 二、效果 效果如下&#xff1a; 三、代码 代码如下&#xff1a; MusicPlayer.vue <template><div class"music"><!-- 占…

【QFD】质量保证需求

QFD体系把前端商业策略成果和产品有效开发紧密连接起来工具。QFD是强调需求与功能的对应&#xff0c;验证确定的市场需求与产品功能设计的关联性&#xff0c;与功能之间&#xff0c;与需求之间的矛盾性&#xff0c;也叫质量屋。 1.什么是QFD 什么是QFD质量功能展开&#xff0…

代码随想录第59天|503.下一个更大元素II ● 42. 接雨水

503.下一个更大元素2 和下一个更大元素基本相同&#xff0c;就多了一个循环数组的问题 处理方法&#xff1a; 循环的次数*2 利用i%nums.size()得到处理的下标&#xff0c;这样当遍历到nums的最后一个元素的时候&#xff0c;向单调栈中插入其下标nums.size()-1,之后将其与下标n…

C语言进阶(7)——联合体和枚举

文章目录1 枚举1.1 含义1.2 定义1.3 枚举的优点1.4 枚举的使用2 联合体&#xff08;共用体&#xff09;2.1 联合类型的定义2.2 联合体的特点2.3联合体大小的计算1 枚举 1.1 含义 枚举就是一一列举。 1.2 定义 枚举是定义常量&#xff0c;默认值 0&#xff0c;然后数自增。 …

go:快速升级Go版本,我几分钟就解决了

底层依赖升级了&#xff0c;那我们也要跟着升&#xff0c;go版本需要升级到1.18以上&#xff0c;网上对比了一些教程&#xff0c;发现这个方法最便捷快速 目录当前Go版本下载高版本Go登录 [Go官网](https://go.dev/dl/)下载对应版本部署升级Go版本备份旧版本部署新版本当前Go版…

从0到1完成一个Node后端(express)项目(一、初始化项目、安装nodemon)

初识express 因为上个专栏的vue项目&#xff0c;后端是用node写的&#xff0c;所以这里教大家怎么去写 Express 简介&#xff1a; Express 是基于 Node.js 平台&#xff0c;快速、开放、极简的 Web 开发框架通俗的理解: Express 的作用和 Node.is内置的 http 模块类似&#xff…

MongoDB副本详解

概念 MongoDB副本集是一组维护相同数据集的mongod服务&#xff0c;可以提供冗余和高可用&#xff0c;是所有生产部署的基础&#xff1b; mysql主从复制和mongodb副本集的区别 主从复制的主从是固定的&#xff0c;副本集是不固定的&#xff0c;可以自动切换 副本集角色 主节点&a…

prototext format 随机空格

prototext format 随机空格问题简述代码复现解密结论总结问题简述 golang 语言&#xff0c;在使用 prototext 进行 format 的时候&#xff0c;相同的代码输出结果不唯一&#xff0c;有的时候字段之间是两个空格&#xff0c;有的时候是一个空格。 代码 先上pb文件 syntax &…

【寒假每日一题】洛谷 P6414 [COCI2014-2015#1] PROSJEK

题目链接&#xff1a;P6414 [COCI2014-2015#1] PROSJEK - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目描述 有一个数列 a&#xff0c;现在按照下列公式求出一个数列 b&#xff1a; 给你数列 b&#xff0c;请求出数列 a。 输入格式 第一行一个整数 n &#xff0c;表示…

bugku-reverse-入门逆向 Easy_vb re 游戏过关 逆向入门

入门逆向下载文件 解压后 拖入IDA中Easy_vb同样方法 解压后 拖入IDAEasy_re下载解压后 打开似乎有点逗查下壳先拖入IDA 选中这两行字符 转译单击a 得到flag游戏过关下载文件 解压打开 发现要把8个都点亮 每次输入会把输入的数本身和他上一个下一个都改变状态&#xff0c;当…

Jenkin权限控制——基于角色授权策略

开启授权策略 Jenkins的项目权限控制通过【授权策略】实现&#xff0c;【授权策略】需要plugins提供&#xff0c;首先需要安装Role-based Authorization Strategy 安装Role-based Authorization Strategy 管理Jenkins——》插件管理——》搜素——》Role-based Authorizatio…

企微机器人使用及内容配置文档

如何使用群机器人 在终端某个群组添加机器人之后&#xff0c;创建者可以在机器人详情页看的该机器人特有的webhookurl。开发者可以按以下说明a向这个地址发起HTTP POST 请求&#xff0c;即可实现给该群组发送消息。下面举个简单的例子. 假设webhook是&#xff1a;https://qyapi…

【Git 从入门到精通】Git中常用的指令(含使用Git维护一个项目的完整操作)

文章目录一、使用git维护一个项目完整操作1.设置用户名与邮箱2.初始化项目中的Git3.使用Git追踪项目中的文件4.提交一次代码5.后期项目的更新与维护附录、常用命令一览表一、使用git维护一个项目完整操作 1.设置用户名与邮箱 注意:这个设置的并不是将代码提交到github或者gite…

CAD动态块操作实例:绘制剖面符号

CAD动态块与普通的CAD图块相比&#xff0c;其图形夹点更多&#xff0c;设计师可以利用动态块的夹点对图形进行快速调整&#xff0c;自由拉伸长度、随心切换隐藏形态等。本节&#xff0c;给大家分享一下浩辰CAD软件中利用CAD动态块的极轴拉伸功能来绘制剖面符号的具体操作步骤。…

[杂记]算法: 并查集

0. 引言 我们考虑如何计算一个图连通分量的个数. 假定简单无向图GGG有两个连通分量(子图)G1,G2G_1, G_2G1​,G2​, 如下图所示: 一个很自然的想法是, 要想求连通分量个数, 我们可以使用Full-DFS算法, 也就是我们从某个点开始深度优先搜索, 并标记访问过的元素. 随后挨个顶点…

高等数学(第七版)同济大学 总习题十一 个人解答

高等数学&#xff08;第七版&#xff09;同济大学 总习题十一 函数作图软件&#xff1a;Mathematica 1.填空&#xff1a;\begin{aligned}&1. \ 填空&#xff1a;&\end{aligned}​1. 填空&#xff1a;​​ (1)第二类曲线积分∫ΓPdxQdyRdz化成第一类曲线积分是_____&am…

Yarn 下载安装及常用配置和命令总结

title: Yarn 下载安装及常用配置和命令总结 date: 2023-01-13 14:47:32 tags: 开发工具及环境 categories:开发工具及环境 cover: https://cover.png feature: false 1. Node.js 建议先安装好 Node.js&#xff0c;见另一篇&#xff1a;Node.js 多版本安装及 NPM 镜像配置_凡 …

Materials - 角色分层材质规范

之前编写的解释性文档&#xff0c;归档发布&#xff1b;在传统贴图中&#xff0c;以BaseColor贴图为例&#xff0c;我们将几乎所有纹理信息都集中到一张贴图上&#xff0c;比如下图中&#xff0c;就有金属、皮革和布料等各种质感的纹理信息&#xff1a;即使是4K的贴图&#xff…

在Win10下装VMware17后,[ 安装VMware Tools ]选项灰色的解决办法

一、说明 菜单【虚拟机】【安装VMware Tools】按钮为灰色&#xff0c;无法实现【安装VMware Tools】的功能&#xff0c;如何解决&#xff0c;使这个功能可以实现&#xff1f;本文介绍此过程。 二、问题发现 在Win10下安装Vmware17后&#xff0c;生成ubuntu18的虚拟机&#xff…