0

All the examples I have seen using SynchronisationContext.Post have been used in the same class. What I have is the UI thread passing some by-ref arguments to a threadwrapper class, updating the arguments and then I want it to update some labels etc on the UIThread.

 internal class ConnThreadWrapper 
{
    ....

    public event EventHandler<MyEventArgs<String, Boolean>> updateConnStatus = 
        delegate { };

    public void updateUIThread(string conn, bool connected)
    {
        uiContext.Post(new SendOrPostCallback((o) => 
                           { 
                               updateConnStatus(this, 
                                  new MyEventArgs<String, Boolean>(conn, 
                                                                   connected));  
                           }), 
                       null);
    }
}


//on ui thread

 public void updateConnStatus(object sender, MyEventArgs<String, Boolean> e)
    {
        switch (e.val1)
        {
           case "CADS" :
           if (e.val2 == true)
           {

           }

The Event seems to fire without any errors but nothing is ever received on the uiThread - i'm not sure if my signature for the sub updateConnStatus is correct or if it works like this. I obviously want the event to handled on the uithread and update the labels from that sub.

In a previous vb.net project I used to reference the form directly on the thread and used a delegate to invoke a callback but apparently this was a bad design as I was mixing application layers. I wanted to use the sync context as it was meant to be thread safe but most of the examples i've seen have used invoke.

Any ideas what I'm missing? Thanks

Peter Lea
  • 1,731
  • 2
  • 15
  • 24
  • Did you set `updateConnStatus` event handler correctly? – tia Aug 20 '12 at 11:51
  • Is there really any advantage of this over using invoke? Like http://www.andreas-reiff.de/2011/06/accessing-a-winformwpf-control-from-another-thread-through-invoke/ or many posts here. At least for that I know that it works cross-thread to update GUIs. It seems like here invoke simply became post? – Andreas Reiff Aug 20 '12 at 11:52
  • Have a look [here](http://stackoverflow.com/a/12029045/706456) – oleksii Aug 20 '12 at 11:56
  • Hi, that was my thread :p @oleksii but the problem is different to my original. I'm using SynchronisationContext rather than invoke as it's meant to be thread Safe I believe. I thought using the SyncContext would also be the easiest as i'm new to Lambdas, Delegates, multithreading etc. – Peter Lea Aug 20 '12 at 12:00
  • @PeterLea ha ha, I just remembered that I saw very similar question but didn't check the user who posted it ;) Well `SynchronisationContext` just allows you to "capture" the "current" thread, execute some load in another thread (this allows your UI thread to process graphics) and "come back" to the "current" UI thread. Have a look at `BacgroundWorker` class, it is specifically designed for UI-Work-UI interactions, it's much easier to use and understand. At least this is where I started from when was learning UI multithreading – oleksii Aug 20 '12 at 13:03
  • Invoke is thread-safe. It works great if e. g. a worker thread needs to update UI-controls. – Andreas Reiff Aug 20 '12 at 18:19

2 Answers2

1

I wrote this helper class which works for me. Prior to using this class call InitializeUiContext() on UI thread somewhere on application start.

   public static class UiScheduler
   {
      private static TaskScheduler _scheduler;
      private static readonly ConcurrentQueue<Action> OldActions = 
          new ConcurrentQueue<Action>();

      public static void InitializeUiContext()
      {
         _scheduler = TaskScheduler.FromCurrentSynchronizationContext();
      }

      private static void ExecuteOld()
      {
         if(_scheduler != null)
         {
            while(OldActions.Count > 0)
            {
               Action a;

               if(OldActions.TryDequeue(out a))
               {
                  UiExecute(_scheduler, a);
               }
            }
         }
      }

      private static void UiExecute(TaskScheduler scheduler, 
                                    Action a, 
                                    bool wait = false)
      {
         //1 is usually UI thread, dunno how to check this better:
         if (Thread.CurrentThread.ManagedThreadId == 1)  
         {
            a();
         }
         else
         {
            Task t = Task.Factory.StartNew(a, 
                                           CancellationToken.None,  
                                           TaskCreationOptions.LongRunning,  
                                           scheduler);

            if (wait) t.Wait();
         }         
      }

      public static void UiExecute(Action a, bool wait = false)
      {
         if (a != null)
         {
            if (_scheduler != null)
            {
               ExecuteOld();

               UiExecute(_scheduler, a, wait);
            }
            else
            {
               OldActions.Enqueue(a);
            }
         }
      }
   }
Kjartan
  • 18,591
  • 15
  • 71
  • 96
Ivan G.
  • 5,027
  • 2
  • 37
  • 65
  • Hi thanks for this, i'll give it a go when i'm back home after work. – Peter Lea Aug 20 '12 at 14:14
  • Hi Guys, just wanted to say I ended up ditching the threadwrapper and looking at Tasks. I'll edit my main post to show how I got it to work. – Peter Lea Aug 20 '12 at 20:02
0

In the end I ditched the ThreadWrapper and trying to marshal the event to the UI Thread and used a Task instead, in fact I think I can use task to do most of the stuff in this project so happy days.

 Task<bool> t1 = new Task<bool>(() => testBB(ref _bbws_wrapper));
 t1.Start();
 Task cwt1 = t1.ContinueWith(task => { if (t1.Result == true) { this.ssi_bb_conn.BackColor = Color.Green;} else { this.ssi_bb_conn.BackColor = Color.Red; } }, TaskScheduler.FromCurrentSynchronizationContext());


.....
private static bool testBB(ref BBWebserviceWrapper _bbwsw)
    {
        try
        {
            //test the connections
            if (_bbwsw.initialize_v1() == true)
            {
                if (_bbwsw.loginUser("XXXXXXXX", "XXXXXXXXX") == true)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return false;
            }

        }
        catch
        {
            return false;
        }
    }
Peter Lea
  • 1,731
  • 2
  • 15
  • 24