0

I am working on a WPF GUI (using MVVM) to control an embedded device. As of yet, the device is still in development and not currently functioning reliably. As such I have created the following fake device:

interface IConnection
{
    bool IsValid { get; }
    bool Open();
    void Close();
    void Write(string message);
}

class SerialConnection : IConnection
{
    // Not yet implemented
}

class DevConnection : IConnection
{
    Timer _timer;
    Action<string> _callback;

    public bool IsValid {...}

    public DevConnection(Action<string> callback)
    {
        _timer = new Timer(tick, null, Timeout.Infinite, Timeout.Infinite);
        _callback = callback;
    }

    public bool Open() {...}
    public void Close() {...}
    public void Write(string Message) {...}

    private void tick(object args)
    {
        _callback("V01" + ToHex(vol1) + "\n");
        ...    
    }
}

The
Action<string> _callback;
is the function used by my model to read the payload of the connection and update its state appropriately

class Model
{
    IConnection _connection;

    public Model()
    {
        _connection = new DevConnection(Message);
    }

    private void Message(string payload)
    {
        ...
        _volume1 = floatValue;
        ...
    }
}

However when the Model is created, I change a bunch of the properties elsewhere before calling Model.IConnection.Open() to start the timer. Every time the Message() callback is called, the debugger shows the Model as still being in its original, constructed state.

1) What is going on behind the scenes here? Is the Threading.Timer creating a new thread for its counting / tick execution? If so, why is it creating a default copy of my Model class?

2) How do I fix it? I even tried giving the DevConnection a copy of my Model class to operate on directly (not how I'd like to setup the architecture) and it still resulted in the same undesired behavior

Unfortunately I have only a rudimentary understanding of the theory of threading, with no idea how to implement it in C#. Tragically I suspect that this issue is a result of thread mis-management.

AGuyInAPlace
  • 359
  • 4
  • 19
  • 2
    `Threading.Timer` callbacks are getting executed on thread pool threads as stated [in the MSDN documentation](https://msdn.microsoft.com/en-us/library/ah1h85ch(v=vs.110).aspx) in the Remarks section, so yes you will be on a different thread than the one you created everything. Regarding the other parts: It would not create by itself copies. This might be just a timing issue / race condition. – Frank J Apr 02 '15 at 17:49
  • 1
    Have you tried putting a breakpoint in the `Model` constructor to see when and where it is constructed. Note that if it is constructed multiple times, also multiple connection and thus timer instances will be created. – Alex Apr 02 '15 at 18:19
  • Do you want multiple threads or do you want to ensure just one? – Conrad Frix Apr 02 '15 at 18:38
  • @Alex Looks like a separate Dev object elsewhere in the code was creating a new Model as well, and assigning that copy to the connection class. Silly mistake. However, I would still like to address the thread issue for stability's sake – AGuyInAPlace Apr 02 '15 at 18:57
  • @ConradFrix It doesn't particularly matter what thread responds to the timer, but I suppose it will be safest if it all occurs in the same thread. This should minimize the need for threadsafing. So I guess the question becomes, how do I ensure that the timer triggers the callback function within the single thread? – AGuyInAPlace Apr 02 '15 at 19:01
  • @AGuyInAPlace I'm glad you were able to resolve your issues (object init, updating the UI). That said you might want to take a look at [this question of mine](http://stackoverflow.com/q/3410648/119477) to resolve *...timer triggers the callback function within the single thread*. Basically there are two options. [Contrive your timer handler to only use one thread](http://stackoverflow.com/a/3409109/119477) use a sync mechanism like `lock()` – Conrad Frix Apr 03 '15 at 17:57

1 Answers1

1

Given that the mysterious "extra copies of the Model class" issue was solved. There remains the issue of how you can safely update your UI from a timer scheduled callback.

As was mentioned by @Frank J, your callback will be invoked on a thread pool thread, whereas it is only allowed to update UI elements from the context of the UI thread. This means you will need to marshal the callback's actions performed in the Message method to the UI thread context if they directly or indirectly update UI elements.

The code snippet below shows one way of doing that.

class Model
{
    private readonly SynchronizationContext _synchronizationContext;
    private readonly IConnection _connection;

    public Model()
    {
         // Capture UI synchronization context.
         // Note: this assumes that Model is constructed on the UI thread.
         _synchronizationContext = SynchronizationContext.Current; 
        _connection = new DevConnection(MessageCallback);
    }

    private void MessageCallback(string payload)
    {
        // schedule UI update on the UI thread.
        _synchronizationContext.Post(
            new SendOrPostCallback(ctx => Message(payload)),
            null);            
    }

    private void Message(string payload)
    {
        ...
        _volume1 = floatValue;
        ...
    }
}

One more piece of advice: I think IConnection should be an IDisposable because you will have to dispose of the timer somewhere.

Alex
  • 13,024
  • 33
  • 62