我正在使用WPF GUI(使用MVVM)来控制嵌入式设备。到目前为止,该设备仍在开发中,目前尚未可靠运行。因此我创建了以下虚假设备:
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");
...
}
}
在
Action<string> _callback;
是我的模型用来读取连接的有效负载并适当更新其状态的函数
class Model
{
IConnection _connection;
public Model()
{
_connection = new DevConnection(Message);
}
private void Message(string payload)
{
...
_volume1 = floatValue;
...
}
}
但是在创建Model时,我会在调用Model.IConnection.Open()来启动计时器之前更改其他一些属性。每次调用Message()回调时,调试器都会将Model显示为仍处于其原始构造状态。
1)幕后发生了什么? Threading.Timer是否为其计数/滴答执行创建了一个新线程?如果是这样,为什么要创建我的Model类的默认副本?
2)我该如何解决?我甚至尝试给DevConnection一个我的Model类的副本来直接操作(而不是我喜欢如何设置架构),它仍然导致了同样的不良行为
不幸的是,我对线程理论只有基本的理解,不知道如何在C#中实现它。可悲的是,我怀疑这个问题是线程管理错误的结果。
答案 0 :(得分:1)
鉴于神秘的“Model
类”问题的额外副本已经解决。仍存在如何从计时器预定回调安全地更新UI的问题。
正如@Frank J所提到的,您的回调将在线程池线程上调用,而只允许从UI线程的上下文更新UI元素。这意味着如果直接或间接更新UI元素,您将需要将回调在Message
方法中执行的操作封送到UI线程上下文。
下面的代码段显示了一种方法。
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;
...
}
}
还有一条建议:我认为IConnection
应该是IDisposable
,因为你必须在某个地方处理计时器。