2011-07-13 12 views
2

私はビットトレントダウンロードプログラムに類似したクライアント/サーバータイプのアプリケーション設定を持っています。ただし、トレントはクライアントにリモートで送信されます。MVVMアプリケーションの並行処理アーキテクチャ

共有データの主な部分は、ダウンロードするファイル(トレント)のリストです。

私は同時にこれらのケースを処理する必要があります。

  • サーバーは、いくつかの新しいファイルがリストに追加される意味、(WCF経由で)ダウンロードするファイルの更新リストを送信し、いくつかは、リストから削除
  • ファイルのダウンロード/状態の変更が完了すると、リスト内の項目を新しい状態でローカルに更新する必要があります。
  • クライアントのローカルイベントにより、リスト内の一部の項目が期限切れになることがあります、削除する必要があります。

私はMVVMアーキテクチャを使用していますが、viewmodelはビューに密接にマップする必要があると思いますので、現在はシングルトンの束である 'サービス'レイヤーを追加しました(私は知っています)。そのうちの1つは前記リストの共有リソースとして機能するので、複数のスレッドによって1つのコレクションが更新されています。

シングルトンから離れて、デッドロック、「削除/デタッチされたオブジェクト」、および私が見ているデータの完全性のエラーを減らすために、依存性注入と不変なオブジェクトを利用したいと考えています。

しかし、私はリストをどこに保つべきか、そしてリストの現在の処理を取り消し/否定/上書きする可能性のある異なるスレッドからの着信イベントを管理する方法についてはほとんど考えていません。

私はこのようなシナリオを高いレベルで処理するための指針を探しています。

データを永続化する必要があるため、リスト内の項目にはEntity Frameworkが使用されています。

答えて

2

私は最近、Windowsサービスチェッカーと同様のことをしました。それはあまりにも簡単に実装することに終わった。

あなたの場合、私は以下の必要性を認識しています。

ファイル - ファイルをダウンロードして変更を通知することのみが目的です。
FileManager - ファイルのリストを管理し、新しいファイルを追加して削除します。

public class File : INotifyPropertyChanged 
    { 
     private readonly string _fileName; 
     private Thread _thread; 
     private Task _task; 
     private bool _cancelled; 

     private TaskStatus _taskStatus; 
     private int _taskProgress; 
     private int _taskTotal; 

     public event PropertyChangedEventHandler PropertyChanged; 

     public File(string fileName) 
     { 
      _fileName = fileName; 
      TaskStatus = TaskStatus.NotStarted; 
     } 

     public TaskStatus TaskStatus 
     { 
      get { return _taskStatus; } 
      private set 
      { 
       _taskStatus = value; 
       PropertyChanged.Raise(this, x => x.TaskStatus); 
      } 
     } 

     public int TaskProgress 
     { 
      get { return _taskProgress; } 
      private set 
      { 
       _taskProgress = value; 
       PropertyChanged.Raise(this, x => x.TaskProgress); 
      } 
     } 
     public int TaskTotal 
     { 
      get { return _taskTotal; } 
      private set 
      { 
       _taskTotal = value; 
       PropertyChanged.Raise(this, x => x.TaskTotal); 
      } 
     } 

     public void StartTask() 
     { 
      _cancelled = false; 

      //.Net 4 - task parallel library - nice 
      _task = new Task(DownloadFile, TaskCreationOptions.LongRunning); 
      _task.Start(); 

      //.Net Other 
      _thread = new Thread(DownloadFile); 
      _thread.Start(); 
     } 

     public void CancelTask() 
     { 
      _cancelled = true; 
     } 

     private void DownloadFile() 
     { 
      try 
      { 
       TaskStatus = TaskStatus.Running; 

       var fileLength = _fileName.Length; 
       TaskTotal = fileLength; 

       for (var i = 0; i < fileLength; i++) 
       { 
        if (_cancelled) 
        { 
         TaskStatus = TaskStatus.Cancelled; 
         return; 
        } 

        //Some work to download the file 
        Thread.Sleep(1000); //sleep for the example instead 

        TaskProgress = i; 
       } 

       TaskStatus = TaskStatus.Completed; 

      } 
      catch (Exception ex) 
      { 
       TaskStatus = TaskStatus.Error; 
      } 
     } 
    } 

    public enum TaskStatus 
    { 
     NotStarted, 
     Running, 
     Completed, 
     Cancelled, 
     Error 
    } 

    public static class NotifyPropertyChangedExtention 
    { 
     public static void Raise<T, TP>(this PropertyChangedEventHandler pc, T source, Expression<Func<T, TP>> pe) 
     { 
      if (pc != null) 
      { 
       pc.Invoke(source, new PropertyChangedEventArgs(((MemberExpression)pe.Body).Member.Name)); 
      } 
     } 
    } 

これは、バックグラウンドスレッドからUIを更新する必要がないという点です。あなたが更新しているのは、バックグラウンドクラスだけが作成する読み込み専用プロパティです。このクラスの外にあるものは何も読み込めないので、ロックを心配する必要はありません。 UIバインディングシステムは、PropertyChangedが呼び出されたときにプロパティが変更されたことを通知し、値を読み取ります。マネージャー

public class FileManager 
    { 
     public ObservableCollection<File> ListOfFiles { get; set; } 

     public void AddFile(string fileName) 
     { 
      var file = new File(fileName); 
      file.PropertyChanged += FilePropertyChanged; 
      file.StartTask(); 
      ListOfFiles.Add(file); 
     } 

     void FilePropertyChanged(object sender, PropertyChangedEventArgs e) 
     { 
      if (e.PropertyName == "TaskStatus") 
      { 
       var file = (File) sender; 
       if (file.TaskStatus==TaskStatus.Completed) 
       { 
        RemoveFile(file);// ??? automatically remove file from list on completion?? 
       } 
      } 
     } 

     public void RemoveFile(File file) 
     { 
      if (file.TaskStatus == TaskStatus.Running) 
      { 
       file.CancelTask(); 
      } 
      //unbind event 
      file.PropertyChanged -= FilePropertyChanged; 
      ListOfFiles.Remove(file); 
     } 
    } 

のための今

は今、あなたはあなたのビューモデルに必要なことは、観測可能なコレクションですFileManagerのからListOfFilesを公開しています。それからの通知は、UIが更新する必要があるときにバインディングシステムに知らせる。

ListOfFilesをListViewなどにバインドするだけで、リストビューに各ファイルのレンダリング方法を知らせるFileクラスのデータテンプレートを追加します。

WCFサーバーとビューモデルには同じファイルマネージャーへの参照があり、WCFはファイルの追加と削除を行い、ViewModelはUIでListOfFilesを使用できるようにします。
これは概念を理解するための荒いハックです。あなたは、あなたが合っていると思うようにあなたのものを追加する必要があります。

これが役立ったかどうか教えてください。

+0

+1よく言われました! @DaveOこれまでと同じアプローチが私のために働いています。これは私が提供したのと同じ答えですが、より明確でよく文書化されています。 FileManagerクラスは、あなたが言及したシングルトンです。私が追加する唯一のことは、Dispatcherを使用してListOfFilesコレクションへの更新がUIスレッド上で確実に行われるようにすることです。 @JohnPetrakが文書化したことは、複数のスレッドからListOfFilesを更新するためにListOfFilesをロックする手間を省きます。 – RMart

+0

ありがとう!これを試すには時間がかかりますが、リファクタリングが完了した後に更新されます。 – DaveO

+0

Add/RemoveFileを別のスレッドからWCFスレッドに呼び出す場合、これらの呼び出しをロックする必要がありますか?また、別のスレッドからListOfFilesを繰り返し処理している場合は、ロックも必要ですか? – DaveO

1

おそらく他の人があなたが探しているクリーナーデザインを考え出すことができますが、問題はマルチスレッドの問題で、固有の設計上の欠陥ではないかと思います。私は実際にシングルトンを使ってリストを管理するというアイデアのようなものですが、ObservableCollectionがこのようなイベントに登録しているモデルやVMに通知する変更のイベントを発生させます。これらのイベントにサブスクライブするときは、Dispatcherを使用してスレッドの同期などを管理します。あなたのシングルトンインスタンスを、それを必要とするモデルまたはVMに注入することを妨げるものは何もありません。

ダウンサイドは、何が起こっているかのような悪夢になる可能性があることを管理していますが、他の選択肢にも同様の面がないかどうかは疑問です。

+0

私は現時点で私のロックを見直していますが、ロックが必要な領域の量が、より良い方法が必要であるという質問をしていると感じています。おそらく、タスクキューイングのいくつかの種類。 – DaveO

+0

@DaveOダウンロードするファイルのリストを管理しようとしているスレッドが複数ありますか?つまり、複数のスレッドで1つのリストを更新していますか?複数のリスト、それぞれ専用のスレッドによって更新されますか? – RMart

+0

複数のスレッドがリストを更新しています。リスト内の個々の項目を更新する複数のスレッド。 – DaveO