Wednesday, June 28, 2017

c# Make some tasks asynchronous

Leave a Comment

My Code:

ConcurrentQueue<string> concurrentQueue = new ConcurrentQueue<string>(); private void Form1_Load(object sender, EventArgs e) {   try {     var task1 = Task.Run(() => GetMessages());     var task2 = Task.Run(() => GetOrderBookData());     UpdateOrderBook();   } catch(Exception ex)   { MessageBox.Show(ex.Message); }  }  private void GetMessages() {   var w = new WebSocketConfiguration(); //class to connect o websocket to get messages   w.OnWSOpen += w_OnWSOpen;   w.OnWSMessage += w_OnWSMessage;   w.OnWSError += w_OnWSError;   w.OnWSClose += w_OnWSClose;   w.SetWebSocketSharpEvents(); // connect to websocket }  void w_OnWSMessage(string message) {   this.Invoke(new MethodInvoker(delegate()   {     listBox1.Items.Add(message);     concurrentQueue.Enqueue(message);     // To refresh the GUI simultaneously as the background process progresses     Application.DoEvents();                    }));             }  private void UpdateOrderBook() {   if (!concurrentQueue.IsEmpty) {     string jsonString;     while (concurrentQueue.TryDequeue(out jsonString))     {     }   } } 

Edit:

private void GetOrderBookData() {   var OrderList = new List<string>();    // Add items using Add method    OrderList.Add("order1");   OrderList.Add("order2");   OrderList.Add("order3");   OrderList.Add("order4");   OrderList.Add("order5");   dgOrders.DataSource = OrderList; } 

In my code UpdateOrderBook is called first and then GetMessages() is called. I want GetMessages() to keep running and once started it should call GetOrderBookData() to fill the values in a grid. Now I want to read messages from the queue and update the grid by calling UpdateOrderBook().

How do I make it asynchronous?

EDIT:

GetMessages() will send me more orders which I have to add/delete in the grid.

EDIT2

Steps: I want to call

(1) GetMessages() this will keep bringing the messages in separate thread

(2) Once the messages starts coming in (1) then call the GetOrderData() this will fetch the order details and insert them in datagrid and the its task is finished here

(3) UpdateOrderBook() this will now update the existing datagrid (which already has some data from step 2). Here in this method I want to check the queue and have to iterate the concurrentqueue then do some processing and then insert that record in the existing grid.

Hope this is clear now. The two process (1,3) would run asynchronously but for the first time it should process in above order.

I got stuck in making them run asynchronously.

4 Answers

Answers 1

Asynchronous methods that you define by using async and await are referred to as async methods. The good thing is that you can rewrite your code to tell the compiler to treat those methods as async. Now, you should be aware that if you don't call the await method inside an asynchronous methods it will not tell the compiler to stop the execution on that point and continue later. They good news is that you can execute the await over your method even if they return just void(Task)

What about this.

TaskFactory.StartNew Method (Action) Creates and starts a task. Parameters:action Type: System.Action The action delegate to execute asynchronously.Documentation Here

        Task task1 = Task.Factory.StartNew(GetMessages);         Task task2 = Task.Factory.StartNew(GetOrderBookData).ContinueWith(t => UpdateOrderBook());          Task.WhenAll(task1, task2).Wait(); 

This is another approach too

void Main() {             var cancelSource = new CancellationTokenSource();              Action action1 = async () =>             {                 await GetMessages();             };              Action action2 = async () =>             {                 //wait for filling the GetOrderBookData                 await GetOrderBookData();                 //on this point you should have the data to refresh on the grid                 UpdateOrderBook();             };              Task task1 = Task.Factory.StartNew(action1, cancelSource.Token);             Task task2 = Task.Factory.StartNew(action2, cancelSource.Token);              //when you want to cancel the task just call Cancel over cancelSource  => cancelSource.Cancel();              Task.WhenAll(task1, task2).Wait(); }  //Be sure to mark your methods with the async keyword private async Task UpdateOrderBook() { ... }  private async Task GetOrderBookData() {  .... }  private async Task GetMessages() {  .... } 

Answers 2

Maybe this will help:

using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms;  namespace WindowsFormsApplication1 {     public partial class Form1 : Form     {         ConcurrentQueue<string> messageQueue = new ConcurrentQueue<string> ();          public Form1 ()         {             InitializeComponent ();         }          private async void Form1_Load (object sender, EventArgs e)         {             var task1 = GetMessages ();             var task2 = Task.Factory.StartNew (GetOrderBookData);              dgOrders.DataSource = await task2;              await Task.WhenAll (task1, task2);              UpdateOrderBook ();         }           public async Task GetMessages ()         {             for (int i = 0; i < 10; i++)             {                 string message = await Task.Run (() => GetMessage (i));                  messageQueue.Enqueue (message);                 listBox1.Items.Add (message);             }         }           private string GetMessage (int id)         {             Thread.Sleep (500); // simulate work             return "some-message-" + id;         }           private IReadOnlyList<string> GetOrderBookData ()         {             Thread.Sleep (2000); // simulate work              return new List<string> { "order 1", "order 2", "order 3" };         }           private void UpdateOrderBook ()         {             string message = null;              string jsonString;              while (messageQueue.TryDequeue (out jsonString))             {                 message += jsonString + "\r\n";             }              MessageBox.Show (message);         }     } } 

Answers 3

Try this.

    private void Form1_Load(object sender, EventArgs e)     {         try         {             //get messages asynchronously             Task getMessagesTask = new Task(GetMessages);             getMessagesTask.Start();              GetOrderBookData();              Task updateTask = new Task(UpdateOrderBook);              //check if concurrent queue has items             if(!concurrentQueue.IsEmpty)             {                 updateTask.Start();             }          }         catch (Exception ex)         { MessageBox.Show(ex.Message); }     } 

Answers 4

To make this code asynchronous you have to do something like this:

private async void Form1_Load(object sender, EventArgs e) {     var t1 = Task.Run(() => GetMessages());     var t2 = Task.Run(() => GetOrderBookData());     var t3 = Task.Run(() => UpdateOrderBook());     await Task.WhenAll(t1, t2, t3); } 

Now, there is a still a problem with your code in that it seems that UpdateOrderBook is dependent on the result of GetMessages. It seems that you are doing some sort of wait and check type loop in UpdateOrderBook. That's not a great idea.

Also the code for GetOrderBookData isn't even in your question.

It would be awesome to have the three methods fully implemented and posted in your question so that we can see how this all hangs together and then we could provide a better solution.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment