【问题标题】:Async Event on C# Best Practice ConfusionC# 最佳实践混淆上的异步事件
【发布时间】:2016-04-24 12:52:51
【问题描述】:

我对有关异步事件的一些最佳实践有一些疑问。更确切地说,将 async 修饰符分配给 void 方法(触发 EventHandler

我的应用应该在后台进行一些测试,并在我完成执行后将结果上传到我的数据库。

在问这个问题之前,我已经查看了here,但我仍然觉得我做错了什么。

根据我的测试,当我调用事件处理程序时,没有明显的理由(对于我的具体情况)应用async 修饰符,因为将async 修饰符指定为subscriber's 底层方法会遇到第一个await 时“回拨”恢复程序执行,直到等待的操作完成,按我的意愿完美运行。

当我开始将async 修饰符应用于 void 方法(WorkerClass)时,我产生了疑问,即使它是事件处理程序,我做错了吗?

我做了以下测试: 制作了一个存储过程,将其执行延迟了大约一分钟

alter procedure TestStoredProcedure
    @WhileValue int
as
begin
    set nocount on;

    begin transaction
    begin try

        waitfor delay '00:01';

        insert into WhileResults(Value)
            values
                (@WhileValue)

        commit tran;
    end try
    begin catch
        raisError('Error',16,1);
        rollback tran;
    end catch;

    return;
end
go

这是我的第一种方法:

class Program
    {
        static void Main(string[] args)
        {
            var workerClass = new WorkerClass();
            var engine = new PlaybackEngine();
            engine.TestFinishedEventHandler += workerClass.WorkSomething;

            engine.TestRun();
        }
    }


    class PlaybackEngine
    {
        public EventHandler TestFinishedEventHandler;

        public void TestRun()
        {
            var i = 0;
            while (i < 10000)
            {
                Console.WriteLine(i);
                i++;    

                OnTestFinishedEventHandler(i,new EventArgs());
            }
        }

        protected virtual void OnTestFinishedEventHandler(object sender, EventArgs args)
        {
            TestFinishedEventHandler?.Invoke(sender,args);
        }

    }

    class WorkerClass
    {
        public async void WorkSomething(object sender, EventArgs args)
        {
            await UploadToDbAsync((int)sender);
        }

        private async Task UploadToDbAsync(int i)
        {
            using (var sqlConn = new SqlConnection("Data Source=EDWARD;Initial Catalog=TestDb;Integrated Security=True"))
            using (var sqlCommand = new SqlCommand())
            {
                sqlConn.Open();
                sqlCommand.Connection = sqlConn;
                sqlCommand.CommandTimeout = 1000000;

                sqlCommand.CommandType = CommandType.StoredProcedure;
                sqlCommand.CommandText = "dbo.TestStoredProcedure";

                sqlCommand.Parameters.AddWithValue("@WhileValue", i);

                await sqlCommand.ExecuteNonQueryAsync();

                sqlConn.Close();
            }
        }
    }

我的第二种方法是:

class Program
    {
        static void Main(string[] args)
        {
            var workerClass = new WorkerClass();
            var engine = new PlaybackEngine();
           // engine.TestFinishedEventHandler += workerClass.WorkSomething;
            engine.TestFinished += workerClass.WorkSomething;

            engine.TestRun();
        }
    }

    class PlaybackEngine
    {
        public delegate Task TestRunEventHandler(object source, EventArgs args);
        public event TestRunEventHandler TestFinished;
        public void TestRun()
        {
            var i = 0;
            while (i < 10000)
            {        
                /* Doing some work here */        
                i++; 
                OnTestRan(i,new EventArgs());
                Console.WriteLine(i);
            }
        }

        protected virtual async void OnTestRan(object source, EventArgs args)
        {
            //await TestFinished?.Invoke(source, EventArgs.Empty);
            if (TestFinished != null)
                await TestFinished(source, new EventArgs());
        }
    }

    class WorkerClass
    {
        public async Task WorkSomething(object sender, EventArgs args)
        {
            await UploadToDbAsync((int)sender);
        }

        private async Task UploadToDbAsync(int i)
        {
            using (var sqlConn = new SqlConnection("Data Source=EDWARD;Initial Catalog=TestDb;Integrated Security=True"))
            using (var sqlCommand = new SqlCommand())
            {
                sqlConn.Open();
                sqlCommand.Connection = sqlConn;
                sqlCommand.CommandTimeout = 1000000;

                sqlCommand.CommandType = CommandType.StoredProcedure;
                sqlCommand.CommandText = "dbo.TestStoredProcedure";

                sqlCommand.Parameters.AddWithValue("@WhileValue", i);

                await sqlCommand.ExecuteNonQueryAsync();
                sqlConn.Close();
            }
        }

【问题讨论】:

    标签: c# tsql events asynchronous event-handling


    【解决方案1】:

    当我开始将(第一种方法)异步修饰符应用于 void 方法(WorkerClass)时,我产生了疑问,即使它是事件处理程序,我做错了吗?

    不,您的做法是正确的,您不应该有任何疑问。 您应该避免使用 async void,除非它是一个事件处理程序。这是唯一可以使用 async void 的地方。

    Async/Await - Best Practices in Asynchronous Programming - 如果您对有关异步编程的更多最佳实践感兴趣,您应该阅读这篇文章。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-19
      • 2010-10-09
      • 1970-01-01
      • 2012-11-12
      • 2011-08-12
      • 2013-02-02
      • 1970-01-01
      • 2018-12-26
      相关资源
      最近更新 更多