之前可能已經問過它的要點,但我完全迷失了所以我正在尋找一些個人指導。一直在嘗試使用 WinForms 和 Yahoo API 為 funsies 制作股票跟蹤器應用程式。嘗試獲取它以便您可以輸入跟蹤器符號,它會創建一個新標簽,該標簽會經常更新自身。但是,它不斷給我關于“跨執行緒操作無效”的錯誤訊息。我試圖做一些谷歌搜索,但是,是的,完全迷失了。這是大部分代碼,希望你們能理解它。
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using YahooFinanceApi;
namespace stockpoging4
{
public partial class Form1 : Form
{
public Form1()
{
System.Globalization.CultureInfo.DefaultThreadCurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo("en-US");
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
using (Prompt prompt = new Prompt("Enter the ticker symbol", "Add ticker"))
{
string result = prompt.Result;
result = result.ToUpper();
if (!string.IsNullOrEmpty(result))
{
do_Things(result);
}
}
}
public async Task<string> getStockPrices(string symbol)
{
try
{
var securities = await Yahoo.Symbols(symbol).Fields(Field.RegularMarketPrice).QueryAsync();
var aapl = securities[symbol];
var price = aapl[Field.RegularMarketPrice];
return symbol " $" price;
}
catch
{
return "404";
}
}
public async void do_Things(string result)
{
string price;
Label label = null;
if (label == null)
{
price = await getStockPrices(result);
label = new Label() { Name = result, Text = result " $" price };
flowLayoutPanel2.Controls.Add(label);
}
else
{
Thread testThread = new Thread(async delegate ()
{
uiLockingTask();
price = await getStockPrices(result);
label.Text = result " $" price;
label.Update();
});
}
System.Timers.Timer timer = new System.Timers.Timer(10000);
timer.Start();
timer.Elapsed = do_Things(results);
}
private void uiLockingTask() {
Thread.Sleep(5000);
}
}
}
uj5u.com熱心網友回復:
讓我指出您的實施中的幾件事。
- 您訂閱 timer.Elapsed after timer.Start 在短計時器間隔的情況下可能無效
- 事件處理程式在后臺呼叫,這就是為什么您不斷收到“跨執行緒操作無效”的原因。UI 組件應該從后臺執行緒正確分派,例如,通過呼叫 flowLayoutPanel2.BeginInvoke(new Action(() => flowLayoutPanel2.Controls.Add(label))); 和 label.BeginInvoke(new Action(label.Update))。此更改已經可以解決您的例外。
- 盡管我會以不同的方式實作此功能,但在這里我發布了稍微更改的代碼,這些代碼通過一些調整正好可以滿足您的需求。
public partial class Form1 : Form
{
Task _runningTask;
CancellationTokenSource _cancellationToken;
public Form1()
{
System.Globalization.CultureInfo.DefaultThreadCurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo("en-US");
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
using (Prompt prompt = new Prompt("Enter the ticker symbol", "Add ticker"))
{
string result = prompt.Result;
result = result.ToUpper();
if (!string.IsNullOrEmpty(result))
{
do_Things(result);
_cancellationToken = new CancellationTokenSource();
_runningTask = StartTimer(() => do_Things(result), _cancellationToken);
}
}
}
private void onCancelClick()
{
_cancellationToken.Cancel();
}
public async Task<string> getStockPrices(string symbol)
{
try
{
var securities = await Yahoo.Symbols(symbol).Fields(Field.RegularMarketPrice).QueryAsync();
var aapl = securities[symbol];
var price = aapl[Field.RegularMarketPrice];
return symbol " $" price;
}
catch
{
return "404";
}
}
private async Task StartTimer(Action action, CancellationTokenSource cancellationTokenSource)
{
try
{
while (!cancellationTokenSource.IsCancellationRequested)
{
await Task.Delay(1000, cancellationTokenSource.Token);
action();
}
}
catch (OperationCanceledException) { }
}
public async void do_Things(string result)
{
var price = await getStockPrices(result);
var label = new Label() { Name = result, Text = result " $" price };
flowLayoutPanel2.BeginInvoke(new Action(() => flowLayoutPanel2.Controls.Add(label)));
}
}
uj5u.com熱心網友回復:
如今,一種更簡單的方法是使用異步。
這是一個觸發Action每個間隔的類:
public class UITimer : IDisposable
{
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
// use a private function which returns a task
private async Task Innerloop(TimeSpan interval, Action<UITimer> action)
{
try
{
while (!_cancellationTokenSource.IsCancellationRequested)
{
await Task.Delay(interval, _cancellationTokenSource.Token);
action(this);
}
}
catch (OperationCanceledException) { }
}
// the constructor calls the private StartTimer, (the first part will run synchroniously, until the away delay)
public UITimer(TimeSpan interval, Action<UITimer> action) =>
_ = Innerloop(interval, action);
// make sure the while loop will stop.
public void Dispose() =>
_cancellationTokenSource?.Cancel();
}
如果您使用 dotnet 3.0 或更高版本,則可以使用 IAsyncDisposable。有了這個,你就可以等待DisposeAsync方法,所以你可以等待_timerTask完成。
我創建了一個新表單,將其作為代碼隱藏:
public partial class Form1 : Form
{
private readonly UITimer _uiTimer;
private int _counter;
public Form1()
{
InitializeComponent();
// setup the time and pass the callback action
_uiTimer = new UITimer(TimeSpan.FromSeconds(1), Update);
}
// the orgin timer is passed as parameter.
private void Update(UITimer timer)
{
// do your thing on the UI thread.
_counter ;
label1.Text= _counter.ToString();
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
// make sure the time (whileloop) is stopped.
_uiTimer.Dispose();
}
}
優點是回呼在 UI 執行緒上運行但不會阻止它。的await Task.Delay(..)是在背景使用定時器,但帖在UI執行緒上的方法/的statemachine的其余部分(因為UI執行緒有一個SynchronizaionContext)
簡單,但有訣竅;-)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/365951.html
下一篇:除特定元素外的串列排序
