执行动作<>在主线程中

时间:2014-01-25 18:24:39

标签: c# multithreading winforms user-interface

我正在编写一个应用程序,它在后台执行非常长的请求。在每个请求之后,我需要将结果发送到主表单。

所以,这是一个代码:

Form1.cs的

private async void StartButton_Click(object sender, EventArgs e)
{
    await Logic.GenerateStackAsync(stackSettings, delegate(FullOrder transaction)
          {
              lastOrderId.Text = transaction.OrderId;
          }
    );

    MessageBox.Show("Completed!");
}

Logic.cs:

    public static bool GenerateStack(StackSettings stackSettings, Action<FullOrder> onOrderCreated = null)
    {
         for(var i = 0; i < 10; i++) 
         {
              // long, long request, replaced with:
              System.Threading.Thread.Sleep(10000);
              if (onOrderCreated != null)
              {
                  onOrderCreated.Invoke(order);
                  // tried to change it with onOrderCreated(order), no results.
              }
          }

          return true;
    }

    public static Task<bool> GenerateStackAsync(StackSettings stackSettings, Action<FullOrder> onOrderCreated)
    {
        return TaskEx.Run(() => GenerateStack(stackSettings, onOrderCreated));
    }

它抛出一个异常:“控制'lastOrderId'从一个线程访问,而不是它创建的线程。”,可以通过添加CheckForIllegalCrossThreadCalls = false;来修复,但我认为这是一个糟糕的经历。怎么做对了?提前谢谢。

P.S。抱歉英语不好。

3 个答案:

答案 0 :(得分:1)

首先,do not expose (fake-)asynchronous wrappers for your synchronous methods

接下来,如果您要报告进度更新,请使用progress update classes provided in .NET for that purpose

public static bool GenerateStack(StackSettings stackSettings, IProgress<FullOrder> progress = null)
{
     for(var i = 0; i < 10; i++) 
     {
          // long, long request, replaced with:
          System.Threading.Thread.Sleep(10000);
          if (progress != null)
          {
              progress.Report(order);
          }
      }

      return true;
}

然后,您可以这样称呼它:

private async void StartButton_Click(object sender, EventArgs e)
{
  var progress = new Progress<FullOrder>(transaction =>
  {
      lastOrderId.Text = transaction.OrderId;
  });
  await Task.Run(() => Logic.GenerateStack(stackSettings, progress));

  MessageBox.Show("Completed!");
}

答案 1 :(得分:-1)

我会说你需要使用Control.Invoke来解决这个问题:

请参阅http://msdn.microsoft.com/library/system.windows.forms.control.invoke(v=vs.110).aspx

答案 2 :(得分:-1)

当你使用async \ await你实际上开始新线程时你会在那里填充你希望结果在主线程中显示UIThread这就是为什么你需要使用Control.Invoke