【问题标题】:.net async abnormal behavior.net 异步异常行为
【发布时间】:2016-08-10 16:00:06
【问题描述】:

我在 WPF MainWindow ctor 上有异常行为。我正在使用异步方法来完成一个长任务,但是这个名为“DoAsync”的任务不完整!我找到了一种解决方法,但我不明白为什么代码会失败:(这里是两个调用和结果:

    /// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    static void WriteToOutput(string message, int increment = 0)
    {
        var nw = DateTime.Now;
        var msg = string.Format("{0}.{1:d3} - thread {2:d1}[0x{3:x4}] |\t{4}{5}",
                                    nw.ToString("hh:mm:ss"),
                                    nw.Millisecond,
                                    System.Threading.Thread.CurrentThread.ManagedThreadId,
                                    AppDomain.GetCurrentThreadId(),
                                    new string(' ', 4 * increment),
                                    message
                                );

        System.Diagnostics.Debug.WriteLine(msg);
    }

    public async Task DoAsync()
    {
        WriteToOutput("DoAsync: begin", 2);
        await Task.Delay(1);                    // enforces asynchronism
        WriteToOutput("DoAsync: job begin", 3);
        System.Threading.Thread.Sleep(5000);    // simulates some job ;)
        WriteToOutput("DoAsync: job end", 3);
        WriteToOutput("DoAsync: end", 2);
    }

    private void NormalBehavior()
    {
        WriteToOutput("NormalBehavior: begin", 0);

        Task.Run(async () =>
        {
            WriteToOutput("NormalBehavior_DoAsync: begin", 1);
            await DoAsync();
            WriteToOutput("NormalBehavior_DoAsync: end", 1);
        });

        WriteToOutput("NormalBehavior: sleeping", 0);
        System.Threading.Thread.Sleep(10000);       // to see what happens
        WriteToOutput("NormalBehavior: terminated", 0);
    }

    /// <summary>
    /// Seems the simplest solution, but it fails :( Why ?
    /// </summary>
    private void AbnormalBehavior()
    {
        WriteToOutput("AbnormalBehavior: begin", 0);

        WriteToOutput("AbnormalBehavior_DoAsync: begin", 1);
        var tsk = DoAsync();
        WriteToOutput("AbnormalBehavior_DoAsync: end", 1);

        WriteToOutput("AbnormalBehavior: sleeping", 0);
        System.Threading.Thread.Sleep(10000);       // to see what happens
        WriteToOutput("AbnormalBehavior: terminated", 0);
    }

    public MainWindow()
    {
        NormalBehavior();
        //  Output:
        //  05:18:00.833 - thread 8[0x3818] |   NormalBehavior: begin
        //  05:18:00.842 - thread 8[0x3818] |   NormalBehavior: sleeping
        //  05:18:00.846 - thread 9[0x2274] |       NormalBehavior_DoAsync: begin
        //  05:18:00.848 - thread 9[0x2274] |           DoAsync: begin
        //  05:18:00.853 - thread 10[0x0778] |              DoAsync: job begin
        //  05:18:05.855 - thread 10[0x0778] |              DoAsync: job end
        //  05:18:05.856 - thread 10[0x0778] |          DoAsync: end
        //  05:18:05.856 - thread 10[0x0778] |      NormalBehavior_DoAsync: end
        //  05:18:10.843 - thread 8[0x3818] |   NormalBehavior: terminated
        //_________________________________________________________________

        AbnormalBehavior();
        //  Output:
        //  05:18:10.845 - thread 8[0x3818] |   AbnormalBehavior: begin
        //  05:18:10.846 - thread 8[0x3818] |       AbnormalBehavior_DoAsync: begin
        //  05:18:10.847 - thread 8[0x3818] |           DoAsync: begin
        //  05:18:10.849 - thread 8[0x3818] |       AbnormalBehavior_DoAsync: end
        //  05:18:10.850 - thread 8[0x3818] |   AbnormalBehavior: sleeping
        //  05:18:20.853 - thread 8[0x3818] |   AbnormalBehavior: terminated
        //_________________________________________________________________

        InitializeComponent();
    }
}

有人已经发现了这个问题或有解释吗?

【问题讨论】:

  • 因为您不要求结果。制作var tsk = DoAsync(); tsk.Result

标签: .net asynchronous synchronization async-await


【解决方案1】:

如果您让它运行更长时间,我希望您会在调试窗口中看到DoWork 的“作业开始”和其他消息。

这是async/await 的预期行为。特别是,await 将捕获当前的“上下文”——在“异常”情况下是 UI 上下文——并将在该上下文中恢复。我在我的博客上更详细地解释了mechanics of async/await

这段代码需要注意的另一点是Thread.Sleep 阻塞了当前线程。特别是当Thread.Sleep在UI线程上运行时,该线程被阻塞,无法执行其他代码。

Task.Run 将在没有 UI 上下文的线程池线程上执行指定代码,在这种情况下,任何 await 都将在线程池线程上恢复。

因此,在您的第一个(“工作”)示例中,DoAsync 在线程池线程上运行并阻塞线程池线程 5 秒。同时,NormalBehavior 阻塞 UI 线程 10 秒。

对于第二个(“失败”)示例,AbnormalBehavior 在 UI 线程上运行 DoAsync。只要DoAsync 击中它的awaitAbnormalBehavior 就会继续运行并阻塞 UI 线程 10 秒。只有这样DoAsync 才能在 UI 线程上恢复并阻塞 5 秒。

【讨论】:

    【解决方案2】:

    斯蒂芬你是对的,在“异常行为”最后一次睡眠之后,我可以看到“DoAsync:作业开始”和“DoAsync:作业结束”都是由主线程完成的! 实际上,它是完成所有工作的同一线程(主线程),正如您所说,它在睡眠时无法完成“工作”:| 更重要的是,在控制台程序上运行的代码与我预期的一样!正如您在这种情况下看到的,它是另一个线程(工作线程)来完成这项工作!也许是因为它不需要捕获上下文!?

    class Program
    {
        static DateTime _Start = new DateTime(0);
    
        static void WriteToOutput(string message, int increment = 0, bool start = false)
        {
            var nw = DateTime.Now;
            if (start) _Start = nw;
            var spn = nw - _Start;
    
            var msg = string.Format("{0,2:d}.{1:d3} - thread {2,2:d1}[0x{3:x4}] |\t{4}{5}",
                                        (int)Math.Floor(spn.TotalSeconds),
                                        spn.Milliseconds,
                                        System.Threading.Thread.CurrentThread.ManagedThreadId,
                                        AppDomain.GetCurrentThreadId(),
                                        new string(' ', 4 * increment),
                                        message
                                    );
    
            if (start) msg = "\n" + msg;
            //System.Diagnostics.Debug.WriteLine(msg);
            Console.WriteLine(msg);
        }
    
        public static async Task DoAsync()
        {
            WriteToOutput("DoAsync: begin", 2);
            await Task.Delay(1);                    // enforces asynchronism
            WriteToOutput("DoAsync: job begin", 3);
            System.Threading.Thread.Sleep(5000);    // simulates some job ;)
            WriteToOutput("DoAsync: job end", 3);
            WriteToOutput("DoAsync: end", 2);
        }
    
        /// <summary>
        /// It works correctly but it uses more resources and run more slowly :o
        /// </summary>
        /// <returns></returns>
        public static Task DoRescueAsync()
        {
            WriteToOutput("DoAsync: begin", 2);
            return Task.Run(() => {
                WriteToOutput("DoAsync: job begin", 3);
                System.Threading.Thread.Sleep(5000);    // simulates some job ;)
                WriteToOutput("DoAsync: job end", 3);
                WriteToOutput("DoAsync: end", 2);
            });
        }
    
        private static void NormalBehavior()
        {
            WriteToOutput("NormalBehavior: begin", 0, true);
    
            Task.Run(async () =>
            {
                WriteToOutput("NormalBehavior_DoAsync: begin", 1);
                await DoAsync();
                WriteToOutput("NormalBehavior_DoAsync: end", 1);
            });
    
            WriteToOutput("NormalBehavior: sleeping", 0);
            System.Threading.Thread.Sleep(10000);       // to see what happens
            WriteToOutput("NormalBehavior: terminated", 0);
        }
    
        /// <summary>
        /// Seems the simplest solution, but it fails :( Why ?
        /// </summary>
        private static void AbnormalBehavior()
        {
            WriteToOutput("AbnormalBehavior: begin", 0, true);
    
            WriteToOutput("AbnormalBehavior_DoAsync: begin", 1);
            var tsk = DoAsync();
            WriteToOutput("AbnormalBehavior_DoAsync: end", 1);
    
            WriteToOutput("AbnormalBehavior: sleeping", 0);
            System.Threading.Thread.Sleep(10000);       // to see what happens
            WriteToOutput("AbnormalBehavior: terminated", 0);
        }
    
        static void Main(string[] args)
        {
            NormalBehavior();
            // Output:
            //   0.000 - thread  1[0x34cc] |    NormalBehavior: begin
            //   0.014 - thread  1[0x34cc] |    NormalBehavior: sleeping
            //   0.019 - thread  3[0x0bdc] |        NormalBehavior_DoAsync: begin
            //   0.024 - thread  3[0x0bdc] |            DoAsync: begin
            //   0.029 - thread  4[0x1568] |                DoAsync: job begin
            //   5.045 - thread  4[0x1568] |                DoAsync: job end
            //   5.050 - thread  4[0x1568] |            DoAsync: end
            //   5.053 - thread  4[0x1568] |        NormalBehavior_DoAsync: end
            //  10.018 - thread  1[0x34cc] |    NormalBehavior: terminated
            //_________________________________________________________________
    
            AbnormalBehavior();     // now CORRECT as expected !
            // Output:
            // 0.000 - thread  1[0x34cc] |    AbnormalBehavior: begin
            // 0.008 - thread  1[0x34cc] |        AbnormalBehavior_DoAsync: begin
            // 0.029 - thread  1[0x34cc] |            DoAsync: begin
            // 0.037 - thread  1[0x34cc] |        AbnormalBehavior_DoAsync: end
            // 0.043 - thread  1[0x34cc] |    AbnormalBehavior: sleeping
            // 0.047 - thread  3[0x0bdc] |                DoAsync: job begin
            // 5.062 - thread  3[0x0bdc] |                DoAsync: job end
            // 7.306 - thread  3[0x0bdc] |            DoAsync: end
            //10.057 - thread  1[0x34cc] |    AbnormalBehavior: terminated
            //_________________________________________________________________
    
            Console.Write("\nHit a key to close console: "); Console.ReadKey();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2011-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-17
      • 1970-01-01
      相关资源
      最近更新 更多