2016-06-17 8 views
10

そこは、C#でマルチコマンド(グループ)をサポートするコマンドパターンの拡張実装は次のとおりです。私は同じを共有していますは、どのようにC#でマルチコマンドパターンの異なるスレッドと同じコンテキストを共有するには?

groups.Execute(); 

var ctx= //the context object I am sharing... 

var commandGroup1 = new MultiItemCommand(ctx, new List<ICommand> 
    { 
     new Command1(ctx), 
     new Command2(ctx) 
    }); 

var commandGroup2 = new MultiItemCommand(ctx, new List<ICommand> 
    { 
     new Command3(ctx), 
     new Command4(ctx) 
    }); 

var groups = new MultiCommand(new List<ICommand> 
    { 
     commandGroup1 , 
     commandGroup2 
    }, null); 

、実行は次のようですコンテキスト(ctx)オブジェクト。

ウェブアプリケーションの実行計画は、異なるスレッドで commandGroup1グループとcommandGroup2グループを分ける必要があります。具体的には、commandGroup2は、メインスレッドで新しいスレッドとcommandGroup1に実行されます。

実行が今のようになります。

//In Main Thread 
commandGroup1.Execute(); 

//In the new Thread 
commandGroup2.Execute(); 

新しいスレッドからcommandGroup1をロールバックすることができるように、どのように私はスレッド安全は、同じcontext object (ctx)を共有することができますか?

は十分t.Start(ctx);または私はロックか何かを使用しなければならないのですか?

いくつかのコードの実装例では、here

+0

それはそのCTXがコマンドで使用されている方法によって異なります。同時に使用する場合(つまり、両方のスレッドが同時にアクセスできる場合)、たとえば、両方のコマンドで変数ctxをロックすることができます。一般的に質問はあまり明確ではありません。多元コマンドをどのように使用するかの具体的な例を提示すると、おそらくより良いでしょう。 – Evk

+0

あなたは並行バッグを調べましたか? –

答えて

1

ICommandのリストを集約し、すべてのコマンドを非同期で実行する必要があるMultiCommandクラスがあるとします。すべてのコマンドはコンテキストを共有する必要があります。各コマンドはコンテキストの状態を変更できますが、設定された順序はありません!

最初の手順は、CTXで渡されるすべてのICommand Executeメソッドを開始することです。次のステップは、新しいCTX変更のイベントリスナーを設定することです。

internal class Command1 : ICommand 
{ 

    public event EventHandler CanExecuteChanged; 

    public bool CanExecute(object parameter) 
    { 
     throw new NotImplementedException(); 
    } 

    public async void Execute(object parameter) 
    { 
     var ctx = (CommandContext)parameter; 
     var newCTX = await Task<CommandContext>.Run(() => { 
      //the command context is here running in it's own independent Task 
      //Any changes here are only known here, unless we return the changes using a 'closure' 
      //the closure is this code - var newCTX = await Task<CommandContext>Run 
      //newCTX is said to be 'closing' over the task results 
      ctx.Data = GetNewData(); 
      return ctx; 
     }); 
     newCTX.NotifyNewCommmandContext(); 

    } 

    private RequiredData GetNewData() 
    { 
     throw new NotImplementedException(); 
    } 
} 

最後に、我々は共通のイベントハンドラと通知システムを設定します。

public class MultiCommand 
{ 
    private System.Collections.Generic.List<ICommand> list; 
    public List<ICommand> Commands { get { return list; } } 
    public CommandContext SharedContext { get; set; } 


    public MultiCommand() { } 
    public MultiCommand(System.Collections.Generic.List<ICommand> list) 
    { 
     this.list = list; 
     //Hook up listener for new Command CTX from other tasks 
     XEvents.CommandCTX += OnCommandCTX; 
    } 

    private void OnCommandCTX(object sender, CommandContext e) 
    { 
     //Some other task finished, update SharedContext 
     SharedContext = e; 
    } 

    public MultiCommand Add(ICommand cc) 
    { 
     list.Add(cc); 
     return this; 
    } 

    internal void Execute() 
    { 
     list.ForEach(cmd => 
     { 
      cmd.Execute(SharedContext); 
     }); 
    } 
    public static MultiCommand New() 
    { 
     return new MultiCommand(); 
    } 
} 

各コマンドは、次のような非同期の一部を処理します。

public static class XEvents 
{ 
    public static EventHandler<CommandContext> CommandCTX { get; set; } 
    public static void NotifyNewCommmandContext(this CommandContext ctx, [CallerMemberName] string caller = "") 
    { 
     if (CommandCTX != null) CommandCTX(caller, ctx); 
    } 
} 

さらに、各コマンドの実行機能で抽象化が可能です。しかし、私たちは今それについて話しません。ここで

は、この設計が行い、行わないものだ:

  1. それは、それが最初MultiCommandクラスに設定されたスレッドで新しいコンテキストを更新する一切の完成タスクが可能になります。
  2. これは、ワークフローベースの状態が必要でないことを前提としています。この記事では、非同期の順番で実行するのではなく、非同期で実行しなければならない作業の束が示されただけです。
  3. 作成されたスレッドで新しいコンテキストを返すために、非同期タスクの各コマンドの終了/完了に頼っているため、currencymanagerは必要ありません。

これは、コンテキストの状態が重要であることを意味します。つまり、そのデザインはこれと似ていますが、異なるものです。その設計は、クロージャの関数とコールバックを使用して簡単に実装されます。

+0

ありがとう。これは私が探していたものです。 –

0

が限り各コンテキストは、単一のスレッドから使​​用されているように、同時に複数のスレッドからそれを使用して、問題がないです。

+0

スレッド間でこのコンテキストオブジェクトを共有する方法の例を提供できますか?t.Start(ctx);を使用するだけです。 ?ロックを使用する必要がありますか? @usr –

+0

また、MultiItemCommandが同じctxオブジェクトを使用しています –

+0

同時使用がないようにする必要があります。あなたが好きな方法でそれをやりなさい。たとえば、 'Task.Run()。Wait(); Task.Run()。Wait(); 'は2つのスレッドを使用する可能性がありますが、同時ではありません。あなたのアーキテクチャに合っていれば、ロックすることもできます。スレッド間の共有は簡単な部分です。あなたはちょうど周囲のコンテキストにオブジェクトrefを渡す必要があります。 – usr

2

提供されているサンプルコードは確かにあなたの特定のユースケースについての質問が多数開いたまま。しかし、私はマルチスレッド環境でこの種の問題を実装するための一般的な戦略に答えようとします。

は、コンテキストまたはそのデータが結合、非atmoic方法で変更を取得していますか?そして、絶対にあなたのコード内のどこかにlock(...)文を利用する必要があるだろう

Context.Data.Item1 = "Hello"; // Setting both values is required, only 
Context.Data.Item2 = "World"; // setting one would result in invalid state 

はたとえば、あなたのコマンドのいずれかのような何かをするだろう。問題はどこにあるのですか?

あなたのネストされたコントローラのスレッド安全行動とは何ですか?リンクされたGISTのサンプルコードで

CommandContextクラスはプロパティServerControllerServiceControllerを有しています。これらのクラスの所有者でない場合は、これらのクラスのスレッドセーフティに関するドキュメントも慎重にチェックする必要があります。

2つの異なるスレッド上で実行しているコマンドのような呼び出し行う場合たとえば、:

Context.ServiceController.Commit(); // On thread A 

Context.ServiceController.Rollback(); // On thread B 

をコントローラクラスの作成者は期待していなかった場合には、これら二つの作用が同時に起動することができないという強い可能性がありますマルチスレッドの使用。

するとロックと何

にロックすることは、あなたがすべてで完全に起こるかをしなければならない複数のアクションを実行する必要がある時はいつでもロックを取る、または同時アクセスを期待していない長時間実行操作を呼び出すときにします。できるだけ早くロックを解除してください。

また、ロックは読み取り専用または一定のプロパティまたはフィールドでのみ行う必要があります。だから、あなたのような何かをする前に:

lock(Context.Data) 
{ 
    // Manipulate data sub-properties here 
} 

Dataが指しているオブジェクトをスワップアウトすることが可能であることを覚えておいてください。

lock(Context.dataSyncRoot) 
{ 
    // Manipulate data sub-properties here 
} 

はロックを行う際に、どこに特効薬はありません:排他的アクセスと使用を必要とする各サブオブジェクトのための

internal readonly object dataSyncRoot = new object(); 
internal readonly object serviceSyncRoot = new object(); 
internal readonly object serverSyncRoot = new object(); 

:最も安全な実装では、特殊なロックオブジェクトを提供することですしかし、一般的には、コールスタックの上位に置くほど、パフォーマンスを犠牲にして、コードがより簡単で安全になります。なぜなら、両方のスレッドが同時に実行できないからです。あなたがそれらを置くほど、コードはより多く並行しますが、より多くの費用もかかります。

脇に:実際のロックの解除と解放にはほとんどパフォーマンスペナルティがないので、それを心配する必要はありません。

+0

私はあなたのアプローチをチェックし、私はあなたに言うでしょう。おかげで –

関連する問題