2

好吧,是时候在这里问我的第一个问题了……

我为它们创建了许多视图(CF 3.5 用户控件,使用 OpenNETCF.IoC 和 resco 控件)和导航控制器。

我还为一个非常奇怪的专有网络设备创建了一个抽象层(“驱动程序”)。命令只能在设备上同步执行,但我使用读取器线程来侦听来自设备的消息(可能随时出现,或响应同步命令)。这些消息可以在事件中分派或排队。

有点像这样,在“驱动程序”界面中:

public Response ExecuteCommand(Command command);<= 这是同步调用。有些命令可能会运行几分钟。

public event DeviceMessageReceivedEvent;<= 如果订阅了这个,传入的消息不是对上述方法的调用的直接回复,将在事件 args 中调度。这将从阅读器线程中调用。

public Queue DeviceMessages;<= 如果未订阅该事件,则此类消息位于此处。

还有一个便利的应用程序外观,但它基本上只是 ExecuteCommand 调用的精美包装器,所以我将在此处省略它。

问题:

我如何最好地将在事件和/或队列中接收到的数据连接到视图,以及如何最好地在 ExecuteCommand 方法上实现异步包装器 - 重申一下,由于设备网络的限制,它必须同步运行协议(是的,这真的很糟糕。)

在阅读了相当多的 .NET CF 3.5 WinForms 异步编程之后,我不知道如何继续。在第一个演示中,我经常使用 DataBindings,但这导致了在需要时没有明显的方法来使用 Control.Invoke 的问题。我还通过仅与 UI 线程同步执行所有命令并使用匿名线程而不是使用事件轮询队列上的消息来解决异步执行问题。由于显而易见的原因,我非常想改变这一点。

CF 不提供 BeginInvoke 和 EndInvoke,但我想这些会很简单,可以重新实现吗?

我应该创建一个以视图为中心的静态模型吗?这甚至会简化问题吗?

我应该创建一个额外的“中间”线程来处理模型/视图和驱动程序之间的状态传输吗?这甚至会简化问题吗?

...?

我想我在这里寻找一些关于一般设计原则的头脑风暴。我从来都不是一个 UI 人,主要是在做系统编程,从来没有比我能避免的更多的 .NET。异步编程的概念很清楚,只是不知道如何在这种情况下应用它们。

所以..任何意见表示赞赏,还有书籍推荐等......

4

2 回答 2

1

听起来很像我目前正在做的一个项目。我有硬件,一次只能发送一个命令。这些命令可能会超时,或者可能只是等到接收器唤醒。命令以回复或未经请求的数据消息的形式出现。

我所做的实际上是创建了一些图层,其中包含一些我现在正在编造的名称

最低层 - 通信总线:

  • 知道如何发送和接收。
  • 知道如何将传入的数据解析为“帧”,但仅此而已(它知道分隔符和校验和)。这实际上是在 OpenNETCF.IO 串行库中的一个缓冲线程中完成的。
  • 将解析的帧放入队列以最小化接收器线程繁忙状态
  • 有一个工作线程来监视 Rx 队列并分派帧
  • 公开一个简单的“发送”接口(发送在上层处理)

下一层 - API (?) 层

设备监视器

  • 知道存在哪些终端设备
  • 知道我上次收到他们的消息是什么时候
  • 从通信总线接收帧并将它们放入队列中(以免阻塞接收器)
  • 知道如何更好地解析帧
  • 引发特定于消息的事件
  • 当节点上线时发送消息(从消息处理程序中检索)
  • 有两个工作线程:
    • TxThread 通过从 que 消息处理程序中提取来处理“一个且只有一个传出消息”问题
    • RxThread 接收 watch 传入的帧队列,解析和分发这些消息

消息管理器

  • 知道哪些消息需要发送到哪些设备
  • 了解每条消息的状态(未发送、等待响应、超时等)
  • 允许过滤“发送”、“超时”等项目

服务层

  • 消费和抽象 API 层
  • 向应用程序公开对象和事件(直接和通过 REST)
  • 是否进行数据聚合(平均值、电池监控等)
  • 包含业务规则/逻辑

这里的重点是我没有明确实现任何异步 API,尽管库中的几乎所有内容在行为上都是异步的。UI 只是为传入的内容查找事件(INotifyPropertyChanged 比比皆是)。对于传出,它会执行类似“请求将引脚 1 设置为高电平”的操作,这是非阻塞的。服务层进行一些检查以验证远程端实际发生的操作,但 UI 只是查看“pin state”属性,当它实际变为高电平时(如在服务层中验证的那样),它会更新。

于 2012-06-18T22:20:58.247 回答
0

我最终得到了一堆特定的 IAsyncResult Begin/End 调用和其他有趣的东西,导致解决方案不是很漂亮或不太适合单核单管道 CPU。但从那以后,我在一个新项目中重新审视了这个问题。在这里,我创建了一个简单的包装器,它允许我轻松地触发和忘记 CF 中的异步调用。它可能并不完美,但它在我需要它做的事情上做得很好。

请注意,这仅适用于 UI 内容。

新架构现在包括一个通用连接层(通过公共接口的串行和 tcp)、一个特定于设备的 API 层(在此级别上几乎没有共同点的几个设备)、一个特定于设备的表示控制器、一个通用表示模型(tuns一旦抽象出来,它们实际上有很多共同点),最重要的是视图,它由表示控制器通过表示模型间接控制。视图对于所有设备(设备特定功能)并不完全相同,但呈现控制器控制视图中可见/加载的内容。

但事不宜迟,

调用示例:

        AsyncCallback callback = delegate(IAsyncResult result)
                                     {
                                         IView2MainContainer.StopWaiting();
                                         try
                                         {
                                             AsyncHelper.EndInvoke(result);
                                         }
                                         catch (Exception ex)
                                         {
                                             RescoMessageBoxHelper.ShowOK(
                                                 string.Format(StringResources.Message_FirmwareUpdateError,
                                                               ex.Message), MessageBoxIcon.Hand);
                                             return;
                                         }
                                         RescoMessageBoxHelper.ShowOK(StringResources.Message_FirmwareUpdateComplete, MessageBoxIcon.Asterisk);
                                     };

        AsyncHelper.BeginInvoke(
            callback,
            delegate
                {
                    IView2MainContainer.StartWaiting(StringResources.Message_WaitFirmwareUpdate);
                    presenterModel.CurrentDevice.UpdatePrinterFirmware(firmwareDataBuffer);
                }
            );

实施:

using System; 
using System.Collections.Generic;
using System.Threading;

namespace Allen.IView2.Common
{
  public static class AsyncHelper
  {
    private static readonly object resultsLock = new object();
    private static volatile List<AsyncResultImpl> results = new List<AsyncResultImpl>();

    public static void EndInvoke(IAsyncResult result)
    {
        if (!(result is AsyncResultImpl))
            throw new InvalidOperationException("The async result was of an unknown type.");

        lock (resultsLock)
        {
            if (!results.Contains((AsyncResultImpl) result))
            {
                throw new InvalidOperationException("The async result was unknown");
            }
            results.Remove((AsyncResultImpl) result);
        }
        ((AsyncResultImpl) result).AsyncWaitHandle.WaitOne();

        Exception ex = ((AsyncResultImpl) result).Exception;
        ((AsyncResultImpl) result).Dispose();

        if (ex != null)
        {
            throw ex;
        }
    }

    public static void BeginInvoke(AsyncCallback callback, Action action)
    {
        var result = new AsyncResultImpl(callback, null);

        lock (resultsLock)
        {
            results.Add(result);
        }

        ThreadPool.QueueUserWorkItem(delegate
                                         {
                                             try
                                             {
                                                 action.Invoke();
                                             }
                                             catch (Exception ex)
                                             {
                                                 result.Complete(ex);
                                                 return;
                                             }
                                             result.Complete();
                                         });
    }
}
}

using System;
using System.Threading;

namespace Allen.IView2.Common
{
  public class AsyncResultImpl : IAsyncResult, IDisposable
  {
    private AsyncCallback callback;
    private Exception exception;
    private object state;
    private ManualResetEvent waitHandle;

    public AsyncResultImpl(AsyncCallback callback, object state)
    {
        this.callback = callback;
        this.state = state;
        waitHandle = new ManualResetEvent(false);
    }

    public ManualResetEvent AsyncWaitHandle
    {
        get { return waitHandle; }
    }

    public Exception Exception
    {
        get { return exception; }
    }

    public object AsyncState
    {
        get { return state; }
    }

    WaitHandle IAsyncResult.AsyncWaitHandle
    {
        get { return AsyncWaitHandle; }
    }

    public bool CompletedSynchronously
    {
        get { return false; }
    }

    public bool IsCompleted
    {
        get { return waitHandle.WaitOne(0, false); }
    }

    public void Dispose() // lazy dispose
    {
        if (null != waitHandle)
        {
            waitHandle.Close();
            waitHandle = null;
            state = null;
            callback = null;
        }
    }

    public void Complete()
    {
        Complete(null);
    }

    public void Complete(Exception exception)
    {
        this.exception = exception;
        waitHandle.Set();
        if (null != callback)
            if (callback.GetInvocationList().Length > 1)
                throw new InvalidOperationException("A callback must not be multicast.");
        if (null != callback)
        {
            try
            {
                callback(this);
            }
            catch
            {
// nom nom
            }
        }
    }
}
}
于 2013-02-27T12:29:34.863 回答