2

我一直在尝试了解有关异步任务和线程的更多信息,但没有取得很大进展。

我正在尝试加载“引擎”类型的线程,该线程将在启动时在后台运行,并且能够访问 UI 线程以更新变量,而无需挂起 UI 线程。

在下面的代码中,Engine 被调用,并创建了一个 Ticker 对象,该对象保存了称为 Last 的 (Litecoin/USD) 的当前值,还保存了其他几个有用的值。此代码成功地将当前值分配给 label1.text。我不一定需要代码,但我会采取什么方法在后台每秒创建一个代码对象并使用每个新的代码对象值更新 UI 线程。

这对后台工作人员来说是一个很好的案例吗?

    private void Form1_Load(object sender, EventArgs e)
    {
        Engine();
    }
    private void Engine()
    {
        Ticker ltcusd = BtceApi.GetTicker(BtcePair.LtcUsd);
        label1.Text = "LTC/USD:" + ltcusd.Last;
    }

编辑: 如果我执行以下操作,label1 会由于跨线程操作尝试(UI 线程中的 label1)而引发 InvalidOperationException。

    private void Form1_Load(object sender, EventArgs e)
    {
        var t = Task.Factory.StartNew(() => Engine());
        t.Start();
    }
    private void Engine()
    {
        while (true)
        {
            Thread.Sleep(1000);
            Ticker ltcusd = BtceApi.GetTicker(BtcePair.LtcUsd);
            label1.Text = "LTC/USD: " + ltcusd.Last;
        }
    }
4

3 回答 3

2

使用async/ await,获取“异步”API 的最简单方法是调用新任务。这不是很好,但它会让事情变得更简单。我可能会创建一个新类,它基本上将所有BtceApi方法都包装在任务中:

public class BtceApiAsync
{
    public Task<Ticker> GetTickerAsync(BtcePair pair)
    {
        return Task.Run(() => BtceApi.GetTicker(pair));
    }

    // etc
}

然后你可以使用一个每秒触发一次的计时器,它将启动一个新任务并适当地更新 UI:

// Keep a field of type System.Windows.Forms.Timer
timer = new Timer();
timer.Interval = 1000;
timer.Tick += DisplayTicker;
timer.Start();

...

private async void DisplayTicker(object sender, EventArgs e)
{
    Ticker ticker = await BtceApiAsync.GetTickerAsync(BtcePair.LtcUsd);
    label1.Text = "LTC/USD: " + ltcusd.Last;
}

请注意,这并不意味着屏幕将每秒更新一次......每秒将启动一次新任务,并且一旦每个任务完成,UI 就会更新。

这里的使用await——从一个在 UI 线程上启动的异步方法——意味着你不需要担心使用 UI;整个异步方法将在 UI 线程上执行,即使 fetch 本身发生在不同的线程中。

于 2013-05-05T19:16:06.143 回答
0

您可以尝试 ContinueWith 在任务结束时更新标签。如果您想在任务结束之前更新它事件,则引发一个在 UI 线程上注册的事件。然后该事件可以更新标签。

于 2013-05-05T19:14:27.617 回答
0

我想这是 Windows 窗体。你可以做到“老派风格”并在 UI 线程上设置标签文本,你可以通过将委托传递给BeginInvokeInvoke方法来做到这一点。

   private void Engine()
   {
      while (true)
     {
        Thread.Sleep(1000);
        Ticker ltcusd = BtceApi.GetTicker(BtcePair.LtcUsd);
        UpdateText("LTC/USD: " + ltcusd.Last);
      }
    }
    private void UpdateText(string text)
    {
        //Inspect if the method is executing on background thread
        if (InvokeRequired)
        {
           //we are on background thread, use BeginInvoke to pass delegate to the UI thread
            BeginInvoke(new Action(()=>UpdateText(text)));
        }
        else
        {
           //we are on UI thread, it's ok to change UI
           label1.Text = text;            
        }
    }
于 2013-05-05T19:19:52.443 回答