2

わかりました、ここで最初の質問をする時間です...

いくつかのビュー (CF 3.5 ユーザー コントロール、OpenNETCF.IoC および resco コントロールを使用) とそれらのナビゲーション コントローラーを作成しました。

また、非常に興味深い専用ネットワーク デバイス用の抽象化レイヤー (「ドライバー」) も作成しました。コマンドはデバイス上で同期的にしか実行できませんが、私はリーダー スレッドを使用して、デバイスからのメッセージ (いつでも、または同期コマンドに応答して来る可能性があります) をリッスンします。これらのメッセージは、イベントでディスパッチするか、キューに入れることができます。

「ドライバー」インターフェースでは、次のようになります。

public Response ExecuteCommand(Command command);<= これは同期呼び出しです。一部のコマンドは数分間実行される場合があります。

public event DeviceMessageReceivedEvent;<= これがサブスクライブされている場合、上記のメソッドへの呼び出しに対する直接の応答ではない受信メッセージは、イベント引数でディスパッチされます。これはリーダースレッドから呼び出されます。

public Queue DeviceMessages;<= イベントがサブスクライブされていない場合、そのようなメッセージはここに表示されます。

便利なアプリケーション ファサードもありますが、これは基本的には ExecuteCommand 呼び出しの派手なラッパーにすぎないため、ここでは省略します。

質問:

イベントおよび/またはキューで受信したデータをビューに接続する最良の方法と、ExecuteCommand メソッドに非同期ラッパーを実装する最良の方法-繰り返しますが、デバイスのネットワークの制限により同期的に実行する必要がありますプロトコル (はい、本当にひどいものです。)

.NET CF 3.5 WinForms を使用した非同期プログラミングについてかなり読んだ後、どのように進めればよいか途方に暮れています。最初のデモでは、DataBindings を多用していましたが、必要なときに Control.Invoke を使用する明確な方法がないという問題が発生しました。また、すべてのコマンドを UI スレッドと同期して実行し、イベントを使用する代わりに匿名スレッドを使用してキュー上のメッセージをポーリングすることで、非同期実行の問題を回避しました。これは、明らかな理由から変更したいと思います。

CF は BeginInvoke と EndInvoke を提供していませんが、それらは再実装するのに十分単純であると思いますか?

静的なビュー中心のモデルを作成する必要がありますか? それは問題を単純化するでしょうか?

モデル/ビューとドライバー間の状態転送を処理するために、追加の「中間」スレッドを作成する必要がありますか? それは問題を単純化するでしょうか?

...?

ここで、一般的な設計原則に関するブレインストーミングを探していると思います。私はあまり UI の人間ではなく、主にシステム プログラミングを行っていました。非同期プログラミングの概念は非常に明確ですが、このコンテキストでそれらを適用する方法はわかりません。

だから..どんな入力も高く評価され、本の推奨事項なども...

4

2 に答える 2

1

サウンドは、私が現在取り組んでいるプロジェクトに非常に似ています。一度に 1 つのコマンドしか送信できないハードウェアがあります。これらのコマンドは、タイムアウトするか、受信機が復帰するまで待機する場合があります。コマンドは、応答として、または未承諾のデータ メッセージとして届きます。

私がやったことは、私が今作っているいくつかの名前で実際にいくつかのレイヤーを作成することでした

最下層 - 通信バス:

  • 送受信する方法を知っています。
  • 着信データを「フレーム」に解析する方法は知っていますが、それ以上のことは知りません (区切り文字とチェックサムを知っています)。これは、実際には OpenNETCF.IO シリアル ライブラリのバッファリング スレッドで行われます。
  • 解析されたフレームをキューに入れ、受信スレッドのビジー状態を最小限に抑えます
  • Rx キューを監視してフレームをディスパッチするワーカー スレッドがある
  • 単純な「送信」インターフェイスを公開します (送信は上のレイヤーで処理されます)

次のレイヤー - API (?) レイヤー

デバイスモニター

  • 存在するエンドデバイスを認識
  • 彼らから最後に聞いたのはいつか
  • 通信バスからフレームを受信し、それらをキューに入れます (受信者をブロックしないように)
  • フレームをより適切に解析する方法を知っている
  • メッセージ固有のイベントを発生させます
  • ノードがオンラインになると、(メッセージ ハンドラから取得したメッセージから) メッセージを送信します。
  • 2 つのワーカー スレッドがあります。
    • TxThread は、キュー メッセージ ハンドラからプルすることにより、「1 つの送信メッセージのみ」の問題を処理します。
    • RxThread は、着信フレーム キューを監視し、それらのメッセージを解析してディスパッチします。

メッセージマネージャー

  • どのデバイスにどのメッセージを送信する必要があるかを把握
  • 各メッセージの状態を把握 (未送信、応答待ち、タイムアウトなど)
  • 「送信予定」、「タイムアウト」などの項目のフィルタリングが可能

サービス層

  • APIレイヤーを消費して抽象化します
  • オブジェクトとイベントをアプリケーションに公開します (直接および REST 経由)
  • データの集計を行います (平均、バッテリー監視など)
  • ビジネス ルール/ロジックを含む

ここでのポイントは、非同期 API を明示的に実装していないことですが、ライブラリ内のほとんどすべての動作が非同期です。UI は単純に、着信するイベント (INotifyPropertyChanged が多数) を探します。発信の場合は、「ピン 1 を高く設定するように要求」のようなことを行いますが、これは非ブロッキングです。サービス レイヤーは、アクションがリモート エンドで実際に発生したことを検証するためにいくつかのチェックを行いますが、UI は単純に「ピン状態」プロパティを確認し、実際に高に変化すると (サービス レイヤーで確認されたように)、更新されます。

于 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 に答える