【问题标题】:Synchronize an async Task in WinForms-Validating-Event在 WinForms-Validating-Event 中同步异步任务
【发布时间】:2020-11-26 10:31:05
【问题描述】:

我有一个经典的 WinForms 应用程序,它与服务器 api 进行异步通信。这是通过异步等待模式实现的。

现在我遇到了“验证”事件的问题。我们将此事件用于客户端验证(检查输入是否为空)并设置取消。 如果输入有效,那么我将输入发送到服务器异步并等待结果。

这是我的问题。现在控制意味着它是有效的。当回调将取消事件设置为 false 时为时已晚。

这是我的代码:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    public string CustomerName { get; set; }

    private async void Button1_Validating(object sender, CancelEventArgs e)
    {
        if (CustomerName == null)
        {
            e.Cancel = true;
        }
        else
        {
            var isValidAndSaved = await SaveOnServerAsync(CustomerName);

            // Here is the Problem: Setting e.Cancel in callback is too late.
            if (isValidAndSaved)
            {
                e.Cancel = false;
            }
            else
            {
                e.Cancel = true;
            }
        }
    }

    /// <returns>True if the given model was valid and save operation was successfull.</returns>
    public async Task<bool> SaveOnServerAsync(string customerName)
    {
        await Task.Delay(3000); // Send given customerName to server in real app.
        return customerName.Contains("%") ? false : true;
    }
}

我通过“等待”主线程中的任务来尝试使用经典编程(同步)。但后来我产生了死锁。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

        public string CustomerName { get; set; } = "3";

    private void Button1_Validating(object sender, CancelEventArgs e)
    {
        if (CustomerName == null)
        {
            e.Cancel = true;
        }
        else
        {
            // Deadlock because MainThread waits for callback. 
            var isValidAndSaved = SaveOnServerAsync(CustomerName).Result;
            if (isValidAndSaved)
            {
                e.Cancel = false;
            }
            else
            {
                e.Cancel = true;
            }
        }
    }

    /// <returns>True if the given model was valid and save operation was successfull.</returns>
    public async Task<bool> SaveOnServerAsync(string customerName)
    {
        await Task.Delay(3000); // Send given customerName to server in real app.
        return customerName.Contains("%") ? false : true;
    }
}

有人想出一个好的模式吗?

如果我在等待异步任务之前将 Cancel 设置为 false,那么我的问题将是用户点击被丢弃并且用户体验足够。

【问题讨论】:

  • 遗憾的是你必须让它同步
  • 您知道如何使其同步吗?如果我尝试它,那么我的 GUI 线程将被冻结(参见第二个代码示例)。
  • 是死锁还是阻塞?无论如何,Validate 事件是一个 UI 验证事件。您的第一张支票 (CustomerName) 是有效支票。另一个调用应该是Click 事件。
  • 在我们的应用程序中,无需按钮即可调用保存命令。当表单失去焦点时保存发生。目标是用户在表单中被捕获,只要它未被保存或中止。表单不在单独的窗口上,而是在与其他表单和网格的停靠元素上。
  • 恕我直言,您在滥用Validating 事件。它旨在验证,而不是提交更改。

标签: c# .net winforms async-await


【解决方案1】:

我找到了一种工作方式。除了第一个任务,所有其他任务都需要“ConfigureAwait(false)”。 请参阅方法“SaveOnServerAsync”。

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        public string CustomerName { get; set; } = "3";

        private void Button1_Validating(object sender, CancelEventArgs e)
        {
            if (CustomerName == null)
            {
                e.Cancel = true;
            }
            else
            {
                var isValidAndSaved = SaveOnServerAsync(CustomerName).Result;
                if (isValidAndSaved)
                {
                    e.Cancel = false;
                }
                else
                {
                    e.Cancel = true;
                }
            }
        }

        /// <returns>True if the given model was valid and save operation was successfull.</returns>
        public async Task<bool> SaveOnServerAsync(string customerName)
        {
            await Task.Delay(3000)
                // The solution is that all deeper Tasks need line below.
                .ConfigureAwait(false);
            ;
            return customerName.Contains("%") ? false : true;
        }
    }

我发现了很多关于这个问题的文章。这听起来有点 MS 的误解。


这篇 MS 文章的图 3 解释了死锁情况: https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming

这里解释了避免死锁的解决方案: https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming#configure-context

【讨论】:

  • “这听起来有点来自 MS 的错误概念” - 他们在设计 Windows 窗体时没有 async/await。向前兼容很难。
  • 我不是指验证事件。我的意思是 ConfigureAwait(false) 是一个错误的概念。我的围兜中的每种方法都需要这种开销。 ViewModel-Commands 中只有一小部分需要 GUI-Sync-Context。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-20
  • 1970-01-01
相关资源
最近更新 更多