我一直在努力了解有关异步任务和线程的更多信息,但并没有取得很大进展。
我正在尝试加载一个“Engine”类型的线程,该线程将在启动时在后台运行,并且能够访问UI线程以更新变量,而不会挂起UI线程。
在下面的代码中,调用Engine,并创建一个Ticker对象,它保存名为Last的(Litecoin / USD)的当前值,还包含其他几个有用的值。此代码成功将当前值分配给label1.text。我不一定需要代码,但我会采用什么方法每秒在后台创建一个ticker对象,并使用每个新的Ticker对象值更新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;
}
修改 如果我执行以下操作,由于跨线程操作尝试(UI线程中的label1),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;
}
}
答案 0 :(得分: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本身发生在不同的线程中。
答案 1 :(得分:0)
您可以尝试使用ContinueWith在任务结束时更新Label。如果要在任务结束之前更新事件,则引发在UI线程上注册的事件。然后事件可以更新标签。
答案 2 :(得分:0)
我想这是Windows Forms。你可以做“旧学校风格”并在UI线程上设置标签文本,你可以通过将委托传递给BeginInvoke或Invoke方法来实现。
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;
}
}