以日志记录功能初窥.net中的异步(Async)

2 篇文章 0 订阅

1、写在前面

最近在研究我们产品的代码,在某些功能(比如说入库)上用到了异步,但这么久一直也没有细细地去研究,网上对相关内容的通俗讲解很多都是以泡茶、洗衣服之类的生活例子,个人认为这对于理解异步的作用没有问题,但真正遇到问题还是不能很好的使用。所以我在学习相关内容的时候也写了一些示例代码,主要的功能是模拟一个耗时的操作,并模拟记录日志(因为我们产品也有响应的功能)。用了两个思路实现,分别是针对.net4.5以上版本的await/async语法糖和低版本.net的IAsyncResult。

2、功能的设计

设计了如下的窗体,两个按钮分别通过await/async和IAsyncResult模拟两个耗时操作,并在底部的文本框中模拟记录日志。
界面设计

3、await/async实现

窗体上方窗体绑定的代码如下:

        private async void button1_ClickAsync(object sender, EventArgs e)
        {
            Class1 class1 = new Class1();
            class1.WriteLogHandle += WriteSystemLogAction;
            int a = await class1.MethodA();
            Notice("最终结果:" + a);
        }

绑定的记录日志方法:

        private void WriteSystemLogAction(string logStr)
        {
            //解决“线程间操作无效”错误,第二种方式
            Action<string> action = new Action<string>(WriteText);
            this.Invoke(action, logStr);
        }
        private void WriteText(string s)
        {
            this.richTextBox1.AppendText(String.Format("\n{0}\t{1}", System.DateTime.Now.ToLongTimeString(), s));
            this.richTextBox1.SelectionStart = richTextBox1.Text.Length;
            richTextBox1.Focus();
        }

最开始没有用委托的方式些,遇到了这个错误,原来,windows窗体控件不是线程安全的,如果几个线程操作某一控件的状态,可能会使该控件的状态不一致,出现争用或死锁状态,所以要用委托机制实现线程安全。当然,也可以初始化控件时设置Control.CheckForIllegalCrossThreadCalls = false;,但微软不推荐这种做法。
线程间操作无效异常
在主窗体的方法中有一个提示的方法:

        private void Notice(string msg)
        {
            MessageBox.Show(msg);
        }

在Class1.cs中,定义了一个写日志的委托,

        //日志记录委托
        public Action<string> WriteLogHandle;

定义一个实例化委托的方法:

        //实例化委托
        private void WriteLog(string log)
        {
            if (WriteLogHandle != null)
            {
                WriteLogHandle(log);
            }
        }

,最后模拟一个耗时操作,并记录日志,模拟的操作是计算累加值,每次计算的时候sleep1秒,并记录每次加的数,最后返回计算结果:

        //await/async示例方法
        public async Task<int> MethodA()
        {
            int result=await Task.Run(()=>{
                int temp = 0;
                for (int i = 0; i < 5; i++)
                {
                    Thread.Sleep(1000);
                    WriteLog("正在计算"+temp+"+" + i.ToString() + ";当前线程" + Thread.CurrentThread.ManagedThreadId.ToString());
                    temp += i;
                }
                return temp;
            });
            return result;
        }

最后运行的结果如下:
await运行结果

大概解释下吧,可能解释得不够深入,不足之处还望指点。在MethodA()方法中使用Task新开一个线程,这个线程用来计算累加值,await关键字在.net官方文档中的介绍是:

await 运算符应用于一个异步方法的任务挂起方法的执行,直到等待任务完成。 任务表示正在进行的工作。
await 表达式不阻止它在其上执行的线程。 相反,它导致编译器注册异步方法的其余部分为等待的任务继续。 然后控件回异步方法的调用方。 当任务完成时,将会调用其延续任务,并且,异步方法的执行恢复它将会停止的位置。

对async的说明是:

异步方法提供了一种简便方式完成可能需要长时间运行的工作,而不必阻止调用方的线程。 异步方法的调用方可以继续工作,而不必等待异步方法完成。

我的理解是,UI线程调用async修饰的异步方法MethodA(),UI线程不会发生阻塞,可以进行其他操作(比如拖动窗体等),可以试试讲这个方法修改成如下,会发现并没有等待计算结果的返回就直接弹出了窗体。

        private async void button1_ClickAsync(object sender, EventArgs e)
        {
            Class1 class1 = new Class1();
            class1.WriteLogHandle += WriteSystemLogAction;
             class1.MethodA();
            Notice("最终结果:");
        }

在MethodA()方法用Task新开一个线程时,result变量等待这个线程的返回结果,结果返回之后,MethodA()方法返回result的值至UI线程,UI线程得到结果弹出提示框。

4、IAsyncResult

IAsyncResult主要是用委托来实现,先写一个方法MethodB()用来模拟耗时操作并记录日志,记录日志的WriteLog方法3中的WriteLog:

        //IAsyncResult方法
        public int MethodB()
        {
            int temp = 0;
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(1000);
                WriteLog("正在计算" + temp + "+" + i.ToString() + ";当前线程" + Thread.CurrentThread.ManagedThreadId.ToString());
                temp += i;
            }
            return temp;
        }

首先,在Class1中定义委托,并在构造方法中实例化委托,绑定Method方法。

        public delegate int AsyncTestDelegate();
        public AsyncTestDelegate asyncTestDelegate;
        public Class1()
        {
            if (asyncTestDelegate == null)
            {
                asyncTestDelegate = MethodB;
            }
        }

接下来写窗体的控件方法,BeginInvoke()方法:

        private void button2_Click(object sender, EventArgs e)
        {
            Class1 class1 = new Class1();
            class1.WriteLogHandle += WriteSystemLogAction;
            IAsyncResult asyncResult = class1.asyncTestDelegate.BeginInvoke(null, null);
            while (!asyncResult.IsCompleted)
            	Application.DoEvents();
            Notice(class1.asyncTestDelegate.EndInvoke(asyncResult).ToString());
        }

其中的while循环主要是避免在等待返回结果时造成卡死状态。
最后的结果也和3中的一样。

5、最后

刚接触异步多线程方面的内容,有些东西确实不够深入,也希望各路高手可以多多指点。相关的代码已上传:
码云下载:https://gitee.com/ranhongwu/dotnet-async-test

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. .NET Core的优势与特点: - 跨平台:可以在Windows、Linux、macOS等操作系统上运行。 - 开放源代码:.NET Core是开源的,可在GitHub上查看和贡献代码。 - 高性能:.NET Core采用了新的运行时和优化技术,能够提供更快的执行速度。 - 简化开发:.NET Core提供了很多常用的API和工具,使得应用程序的开发变得更加简单。 - 模块化:.NET Core可以根据需要选择和加载所需的模块,减少不必要的依赖。 2. 对于C#的委托(Delegate)的了解: 委托是一种类型安全的函数指针,它可以将一个或多个方法封装成一个委托实例,然后将该实例传递给其他方法作为参数或存储在变量,从而实现方法的回调。 3. MVC架构模式的组件: - Model:模型层,用于表示应用程序的数据和业务逻辑。 - View:视图层,用于展示数据和与用户交互。 - Controller:控制器层,用于接收用户请求并处理它们,然后调度适当的模型和视图来响应请求。 4. 对于.NET的反射机制(Reflection)的了解: 反射机制是指在运行时动态地获取和操作程序元素的能力,如类型、方法、属性、字段等。它可以用来实现很多高级功能,如动态加载程序集、创建对象、调用方法、获取属性等。 5. 数据库事务(Transaction)的理解和使用注意事项: 事务是一组相关的数据库操作,要么全部执行成功,要么全部回滚。它可以保证数据的一致性和可靠性。使用事务时需要注意以下几点: - 事务应该尽量的短。 - 事务的操作应该尽量简单,避免复杂的逻辑。 - 在事务应该尽量使用索引,避免锁表。 - 对于长时间运行的事务,应该考虑设置超时时间。 6. 对于SignalR的使用经验: SignalR是一种实时通信库,可以使得服务器端和客户端之间的通信更加简单和高效。在项目,可以使用SignalR实现实时聊天、通知、在线用户列表等功能。通常使用Hub来处理客户端和服务器之间的通信。 7. 对于MongoDB的使用经验: MongoDB是一种文档型数据库,可以存储和查询JSON格式的数据。在使用MongoDB时,需要先创建数据库和集合,然后可以使用C#的驱动程序来进行数据的增删改查等操作。在项目,可以使用MongoDB来存储用户信息、日志、配置等数据。 8. 对于异步编程(Async Programming)的了解和实现方式: 异步编程是指通过异步操作来提高程序的响应性和性能。在.NET,可以使用async和await关键字来实现异步编程,从而避免线程阻塞和提高程序的吞吐量。 9. 对于.NET的依赖注入(Dependency Injection)的了解和使用方式: 依赖注入是一种通过构造函数、属性或方法参数来注入依赖项的技术,可以提高代码的可测试性和可维护性。在.NET,可以使用DI容器来管理依赖项,如ASP.NET Core的内置DI容器或第三方的Autofac、Ninject等。 10. 对于.NET的LINQ的了解和使用方式: LINQ是一种语言集成查询(Language Integrated Query)技术,可以通过类似于SQL的语法来查询各种数据源,如集合、数组、XML、数据库等。在.NET,可以使用LINQ来进行数据过滤、排序、分组、投影等操作,从而简化代码并提高开发效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值