【问题标题】:C# - Cross Thread Error using Async and Await on TextBoxC# - 在 TextBox 上使用 Async 和 Await 的跨线程错误
【发布时间】:2018-11-23 03:54:41
【问题描述】:

我是 Async 和 Await 的新手,我创建了一个简单的项目来了解它的工作原理。 为此,我有一个简单的 Windows 窗体应用程序,它有 2 个元素:

  • 获取已完成的项目按钮
  • 显示检索到的所有已完成项目的文本框

当我单击按钮时,它应该在 TextBox 中显示所有已完成的项目。 这是我写的代码:

private async void btnGetCompletedItems_Click(object sender, EventArgs e)
{
    QueueSystem queueSystem = QueueSystem.NewInstance(75);

    Stopwatch watch = new Stopwatch();

    watch.Start();
    await Task.Run(() => GetCompletedItems(queueSystem));
    watch.Stop();

    lblTime.Text = $"{watch.ElapsedMilliseconds.ToString()} ms";

}

private void GetCompletedItems(QueueSystem queueSystem)
{
    foreach (var item in queueSystem.GetCompletedItems())
    {
        txtItems.Text += $"{txtItems.Text}{item.ItemKey}{Environment.NewLine}";
    }
}

但是,我在

中遇到错误

txtItems.Text += $"{txtItems.Text}{item.ItemKey}{Environment.NewLine}";

错误提示

附加信息:跨线程操作无效:控制 'txtItems' 从创建它的线程以外的线程访问 开。

我检查了 Debug 并为 GetCompletedItems() 创建了一个新线程。当我读到 Async 和 Await 时,我读到它不一定会创建一个新线程,但它似乎出于某种原因创建了一个新线程。

我对 Async 和 Await 的实现和理解错了吗? 是否可以在 Windows 窗体应用程序中使用 Async 和 Await?

【问题讨论】:

标签: c# multithreading winforms asynchronous async-await


【解决方案1】:

您无法访问不同线程上的 UI 线程。这应该会有所帮助

private async void btnGetCompletedItems_Click(object sender, EventArgs e)
{
    QueueSystem queueSystem = QueueSystem.NewInstance(75);

    Stopwatch watch = new Stopwatch();

    watch.Start();
    var results = await Task.Run(() => queueSystem.GetCompletedItems());
    foreach (var item in results)
    {
        txtItems.Text += $"{txtItems.Text}{item.ItemKey}{Environment.NewLine}";
    }
    watch.Stop();

    lblTime.Text = $"{watch.ElapsedMilliseconds.ToString()} ms";

}

【讨论】:

    【解决方案2】:

    当我读到 Async 和 Await 时,我读到它不一定会创建一个新的 线程

    这适用于常规异步方法。考虑一下:

        private async void button1_Click(object sender, EventArgs e)
        {
            Trace.WriteLine(Thread.CurrentThread.ManagedThreadId);
    
            await DoesNothing();
        }
    
        private async Task DoesNothing()
        {
            // outputs the same thread id as similar line as from above;
            // in particlar, for WinForms this means, that at this point
            // we are still at UI thread
            Trace.WriteLine(Thread.CurrentThread.ManagedThreadId);
    
            await Task.Delay(1);
        }
    

    但它似乎出于某种原因创建了一个新的

    这就是Task.Run 的用途:

    将指定的工作排入队列以在 ThreadPool 上运行

    换句话说,它推送任何你作为委托传递给线程池线程的东西。由于我们在 WinForms 中,这意味着匿名方法 () => GetCompletedItems(queueSystem) 将在线程池线程中执行,而不是在 UI 之一。

    这是上面的代码示例,改动不大:

        private async void button1_Click(object sender, EventArgs e)
        {
            Trace.WriteLine(Thread.CurrentThread.ManagedThreadId);
    
            await Task.Run(DoesNothing);
        }
    
        private async Task DoesNothing()
        {
            // outputs DIFFERENT thread id;
            // in particlar, for WinForms this means, that at this point
            // we are not at UI thread, and we CANNOT access controls directly
            Trace.WriteLine(Thread.CurrentThread.ManagedThreadId);
    
            await Task.Delay(1);
        }
    

    【讨论】:

      【解决方案3】:

      您可以通过以下方式从另一个线程访问该线程。它确实有助于避免应用程序中的跨线程异常。

      private void Thread()
      {
          this.Invoke((System.Action)(() => {
              //your thread call or definition
          });
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多