C#最佳实践混乱的异步事件

时间:2016-04-24 12:52:51

标签: c# tsql events asynchronous event-handling

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

我的应用程序应该在后台进行一些测试,当我完成执行以将结果上传到我的Db时。

在提出问题之前,我已经查看here,但我仍然觉得我做错了。

根据我测试的结果,在我调用事件处理程序时,没有明显的理由(针对我的具体情况)应用async修饰符,因为将async修饰符指定为{{ 1}}潜在的方法,它会拨回'遇到第一个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();
            }
        }

1 个答案:

答案 0 :(得分:3)

  

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

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

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