1

ユーザーが作業を中止するかどうかを定期的に確認する必要があるバックグラウンド スレッドでコードを実行しています。

問題は、ほとんどSOLID環境でこれを実装する方法です。

  • ICancelすべてのクラスに挿入するインターフェイス。
  • どこかの public static メンバー

非定数静的は、遅かれ早かれ常に問題を引き起こすようです。一方、「標準」のインジェクション (ILogger、IProgressReporter など) の数は着実に増加しているため、cancel のような単純なものは static を使用するのに適している可能性があります。

他の/より良い方法はありますか? 誰でも共有する経験がありますか?

私は WPF と C# を使用していますが、質問は一般的です。


次に例を示します。

// this code is in some model assembly

public class BackgroundWorkFactory {
    public IDoingBackgroundWork Worker { 
        get { return new DoingBackgroundWork(new Whatever()); }
    }

internal class DoingBackgroundWork : IDoingBackgroundWork {
    public DoingWork(IWhatever whatever) {
        mWhatever = whatever;
    }
    public void WorkThatCanBeCanceled() {
        while (!Canceled && somethingElse) {
            mWhatever = whatever.DoSomthingElseThatMightAlsoAllowCancel();
            ...
        }
    }
}


// This code is in the GUI Assembly

public void StartWork() {
    IDoingBackgroundWork work = factory.Worker;
    Thread t = new Thread(work->WorkThatCanBeCanceled());
    t.Start();
}

public void StopWork() {
   // ??
}
4

4 に答える 4

0

いくつか試した後、このように解決しました。

StopRequest クラスを作成しました (インターフェースの必要性はわかりません)

public class StopRequest
{
    public void RequestStop() {
        mIsStopRequested = true;
    }

    public void Reset() {
        mIsStopRequested = false;
    }

    public static implicit operator bool(StopRequest stopRequest) {
        return stopRequest.mIsStopRequested;
    }

    private volatile bool mIsStopRequested;
}

このクラスを必要とする各クラスに注入します (またはメソッド引数として渡します)

public void StartWork() {
    mStopRequest = new StopRequest();
    IDoingBackgroundWork work = factory.Worker(mRequestStop);
    mThread = new Thread(work->WorkThatCanBeCanceled());
    mThread.Start();
}

public void StopWork() {
    mStopRequest.RequestStop();
    mThread.Join(timeout);
}

//-----

public class BackgroundWorkFactory {
    public IDoingBackgroundWork Worker(StopRequest stopRequest) { 
        return new DoingBackgroundWork(stopRequest, new Whatever(stopRequest));
    }
}

internal class DoingBackgroundWork : IDoingBackgroundWork {
    public DoingBackgroundWork(StopRequest stopRequest, IWhatever whatever) {
        mStopRequest = stopRequest;
        mWhatever = whatever;
    }

    public void WorkThatCanBeCanceled() {
        while (!mStopRequest && somethingElse) {
            x = mWhatever.DoSomthingElseThatMightAlsoAllowCancel();
            ...
        }
    }
}

これには次の利点が
あります - static/singleton オブジェクトが不要
- キャンセル機能を持​​つクラスのみが影響を受けます
- DI フレームワークはおそらくそれを自動的に注入できます
- 単体テスト時の複雑なセットアップは不要
です - 軽量/高速

于 2009-09-15T09:37:44.173 に答える
0

ここで少し迷っているかもしれませんが、なぜ IDoingBackgroundWork は単純な Cancel メソッドを宣言できないのでしょうか。

そうすれば、ワーカー スレッドを呼び出し元からキャンセルする簡単な方法、デフォルトの実装、およびその実装を変更する機能が得られます。これに注射を使用する必要は本当にありますか?古き良き「シンプルな」オブジェクト指向パターンがそれを正しくカバーしていると思います。

アップデート:

それから私はどちらのアプローチも好きではありません。IWhatever は、ある時点で正常に停止する必要があることを知る必要があるため、コードを挿入して感謝の気持ちで終了することを期待することはできず、静的クラスは同じレベルの結合を追加します (つまり、IWhatever はその静的クラスを知って使用する必要があります)。

ICancelable インターフェイスを定義してから、次のようにします。

class DoingBackgroundWorkBase
{
  public Cancel()
  {
    ICancelable cancelableWork = mWhatever as ICancelable;
    if (cancelableWork != null)
      cancelableWork.Cancel();
    else
      this.Abort();
  }  
}

そしてもちろん

IDoingBackgroundWork work = factory.Worker;
work.Start();

そのため、IDoingBackgroundWork は手動でスレッドを開始する (またはおそらくスレッド自体である) ことを担当し、IWhatever が ICancelable も実装していない場合に「優雅でない」種類の終了を提供します。

于 2009-08-28T08:28:53.710 に答える
0

また、呼び出された各クラスで true のときに実行さvolatileれるフラグを単純に作成することもできます。UserHasWorkユーザーがキャンセルした場合は、フラグを確認してスレッドを停止します。

ただし、これはインターフェイスでは使用できませんが、やりたいことを実行するのに便利な方法だと思います。抽象クラスでも常に使用できますが、そのアプローチでは短所が長所を上回ります。

于 2009-08-28T07:08:56.070 に答える
0

IIUC さんは、すべてのバックグラウンド スレッドをキャンセルする静的手段を提案しています。これは十分にモジュール化されておらず、各スレッドを個別にキャンセルできるようにする必要があると思います。

OTOH、ICancel インターフェイスだけでは、あまり再利用できません。クラスを提案します

class CancelThread{
  private boolean cancelled;
  synchronized void cancel(){cancelled = true;}
  synchronized boolean isCancelled(){return cancelled}
}

各スレッドにはこれらのいずれかがあり、おそらくそのようなオブジェクトのグローバル セットも多数あります。したがって、すべてのスレッドをキャンセルするには、セットを繰り返し処理し、すべてのスレッドに対してキャンセルを呼び出します。

于 2009-08-28T07:12:59.730 に答える