代码之家  ›  专栏  ›  技术社区  ›  Poul Bak

如何在使用async/await[duplicate]时避免UI线程上的竞争条件

  •  2
  • Poul Bak  · 技术社区  · 6 年前

    我们都听说保持UI线程的响应非常重要,所以我们实现了async/await everwhere。 我正在构建一个文本编辑器,其中的“一切”都是异步的。但是,现在我发现当代码在其他代码完成之前运行时,它会受到UI线程上的竞争条件的影响。 当然,这就是“响应式UI线程”的全部思想,即它可以运行代码,同时唤醒其他代码。 我需要一些代码来等待其他代码在运行之前完成。我将问题归结为以下代码:我只需处理击键:

        private async void Form1_KeyPress(object sender, KeyPressEventArgs e)
        {
            //Wait until it's your turn (await HandleKey has returned) before proceeding
            await HandleKey(e.KeyChar);
        }
    
        async Task HandleKey(char ch)
        {
            await GetCaretPosition();
            Point newPosition = await Task.Delay(1000);
            await SetCaretPosition(newPosition);
        }
    

    正如您所看到的,当第一个键被处理(唤醒)时,下一个键可以开始处理。在这个简单的例子中,第二个密钥处理代码将得到一个旧的caretposition值, 因为第一个密钥处理尚未更新caretposition。 如何使按键事件中的代码等到第一个按键完成处理? 返回同步编码不是一个选项。

    2 回复  |  直到 6 年前
        1
  •  0
  •   Poul Bak    6 年前

    对于浏览本文的人:这是我根据k1dev关于 SemaphoreSlim (读一下 here .

    这真的很容易。根据问题中的代码,我添加了 信号灯 并等待它(异步)。一个 信号灯 是一个轻量级的 Semaphore 特别为在UI线程上等待而制造。我用一个 Queue<char> 要确保按正确的顺序处理密钥:

    SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
    Queue<char> queue = new Queue<char>();
    
    private async void Form1_KeyPress(object sender, KeyPressEventArgs e)
    {
        queue.EnQueue(e.KeyChar);
        await semaphore.WaitAsync();
        try
        {
           await HandleKey(queue.DeQueue());
        }
        finally
        {
            semaphore.Release();
        }
    }
    

    另外:我有一个方法,如果应用程序很忙,我只想跳过它,可以使用以下代码:

    if (await semaphore.WaitAsync(0)) //Note the zero as timeout
    {
        try
        {
           await MyMethod();
        }
        finally
        {
            semaphore.Release();
        }
    }
    
        2
  •  -1
  •   Alexander Toptygin    6 年前

    首先,听起来这个问题还有很多,或者可能会有后续行动。

    也就是说,您可以尝试使HandleKey同步以管理共享资源:

    [MethodImpl(MethodImplOptions.Synchronized)]
    async Task HandleKey(char ch)
    {
    

    请尝试此线程以供参考: C# version of java's synchronized keyword?

    PS-所以我越是考虑这个问题,就越觉得你试图同步执行某个工作单元,但却把它从ui线程上卸下。也就是说,同步HandleKey方法不会达到预期的效果。我认为您可能正在寻找一种调度程序模式:

    https://www.what-could-possibly-go-wrong.com/the-dispatcher-pattern/

    https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher(v=vs.110).aspx