Tarea asincrónica de C# para un tablero de cotizaciones

I've been trying to learn more about asynchronous tasks and threading but not making a ton of headway.

I'm trying to load an "Engine" type of thread that will run in the background upon launch and be able to access the UI Thread to update variables, without hanging the UI Thread.

In the below code, Engine is called, and a Ticker object is created which holds the current value of (Litecoin/USD) called Last, also holds several other values that would be useful. This code successfully assigns the current value to label1.text. I don't necessarily need code but what approach would I take to create a ticker object in the background every second and update the UI thread with each new Ticker objects values.

Is this a good case for a background worker?

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

EDIT: If I do the following, label1 throws an InvalidOperationException due to a Cross-thread operation attempt (label1 in the UI thread).

    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;
        }
    }

preguntado el 05 de mayo de 13 a las 19:05

Are you using .NET 4.5 and C# 5? -

Yes (enter atleast 15 characters) -

Y es BtceApi something under your control, or not? (Ideally, you'd make as much as possible asynchronous, so that you don't even need another thread.) -

It's available here, I can modify it. I did not write it though, obviously. github.com/DmT021/BtceApi -

Hmm... no obvious async API then. You could launch a separate task I guess... -

3 Respuestas

Usar async/await, the simplest way of getting an "asynchronous" sort of API is to invoke a new task. It's not great, but it'll make things simpler. I would probably create a new class which basically wrapped all the BtceApi methods in tasks:

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

    // etc
}

Then you can use a timer which fires once per second, which will start off a new task and update the UI appropriately:

// 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;
}

Note that this doesn't mean the screen will be actualizado once per second... there will be a new tarea started once per second, and as soon as each task completes, the UI will be updated.

El uso de await here - from an async method started on the UI thread - means you don't need to worry about using the UI; the whole async method will execute on the UI thread, even though the fetch itself happens in a different thread.

contestado el 05 de mayo de 13 a las 20:05

Awesome, this approach makes sense to me. Thanks for your time. - user2163343

You can try ContinueWith to update the Label at the end of the task. If you want to update it event before the task ends then raise an event which is registered by on the UI thread. The event can then update the label.

contestado el 05 de mayo de 13 a las 20:05

I suppose this is Windows Forms. You could do it "old school style" and set the label text on the UI thread, and you can do that by passing delegate to the BeginInvoke or invocar método.

   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;            
        }
    }

contestado el 05 de mayo de 13 a las 20:05

I wish I could upvote but apparently I don't have enough rep yet, this worked as well. Thanks for the help. - user2163343

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.