3

排他的な方法で実行する必要があるメソッドがあります。基本的に、これはメソッドがタイマーによって定期的に呼び出されるマルチスレッド アプリケーションですが、ユーザー アクションによって手動でトリガーすることもできます。

例を見てみましょう:

  1. タイマーが経過したため、メソッドが呼び出されます。このタスクには数秒かかる場合があります。

  2. 直後に、ユーザーがいくつかのボタンをクリックすると、同じタスクがトリガーされます: BAM. メソッドはすでに実行されているため、何もしません。

私は次の解決策を使用しました:

public void DoRecurentJob()
{
    if(!Monitor.TryEnter(this.lockObject))
    {
        return;
    }

    try
    {
        // Do work
    }
    finally 
    {
        Monitor.Exit(this.lockObject);
    }
}

lockObjectそのように宣言されている場所:

private readonly object lockObject = new object();

編集: このメソッドを保持するオブジェクトのインスタンスは 1 つしかないため、ロック オブジェクトを非静的になるように更新しました。

それを行うより良い方法はありますか?それとも、これは何らかの理由で間違っているのでしょうか?

4

8 に答える 8

4

メソッドを並行して実行しないことに関心がある場合、これは合理的に見えます。タイマーが Monitor.Exit() を実行してから 0.5 マイクロ秒後にボタンを押したとします。

また、ロック オブジェクトを readonly static にすることも理にかなっています。

于 2008-09-29T09:36:39.383 に答える
2

Microsoftは、Monitorクラスを直接使用するのではなく、 lockステートメントを使用することをお勧めしていると思います。それはよりすっきりとしたレイアウトを提供し、すべての状況でロックが解放されることを保証します。

public class MyClass
{

  // Used as a lock context
  private readonly object myLock = new object();

  public void DoSomeWork()
  {
    lock (myLock)
    {
      // Critical code section
    }
  }
}

アプリケーションでMyClassのすべてのインスタンスにまたがるロックが必要な場合は、ロックコンテキストを静的フィールドとして定義できます。

private static readonly object myLock = new object();
于 2008-09-29T10:34:35.873 に答える
2

クロスプロセスで動作させたい場合 (パフォーマンスがわずかに低下します)、またはコードを実行する許可された同時スレッド以外の数を設定する必要がある場合は、 or をMutex使用することもできます。Semaphore

他にも機能するシグナリング構造がありますが、あなたの例は、単純で簡単な方法でうまくいくように見えます。

于 2008-09-29T09:36:53.297 に答える
2

マイナーな問題: lockObject 変数が静的である場合、"this.lockObject" はコンパイルされません。また、これはインスタンス メソッドであるにもかかわらず、明確に型全体の動作を行うことも少し奇妙に感じられます (少なくとも十分に文書化する必要があります)。インスタンスをパラメーターとして取る静的メソッドにすることはできますか?

実際にインスタンスデータを使用していますか? そうでない場合は、静的にします。そうであれば、少なくともそのインスタンスで作業を行ったかどうかを示すブール値を返す必要があります。特定のデータに対して何らかの作業を行いたいという状況を想像するのは難しいと思いますが、私はしません。同様の作業が別のデータで実行されていたために、その作業が実行されなかった場合は注意してください。

うまくいくはずだと思いますが、少し奇妙に感じます。私は通常、手動ロックを使用するのが好きではありません。なぜなら、それは間違いやすいからです - しかし、これは問題ないようです. (「if」と「try」の間の非同期例外を考慮する必要がありますが、問題にはならないと思います-CLRによって行われた正確な保証を思い出せません。)

于 2008-09-29T09:39:13.777 に答える
1

コードは問題ありませんが、意図をよりよく伝えるため、メソッドを静的に変更することに同意します。クラスのすべてのインスタンスが、それらの間に同期的に実行されるメソッドを持っているのに、そのメソッドが静的ではないというのは奇妙に感じます。

静的同期メソッドを常に保護またはプライベートにすることができ、クラスのインスタンスにのみ表示されることを忘れないでください。

public class MyClass
{ 
    public void AccessResource()
    {
        OneAtATime(this);
    }

    private static void OneAtATime(MyClass instance) 
    { 
       if( !Monitor.TryEnter(lockObject) )
       // ...
于 2008-09-29T10:02:10.787 に答える
0

これは良い解決策ですが、静的ロックにはあまり満足していません。今はロックを待っていないので、デッドロックの問題に巻き込まれることはありません。ただし、ロックを目立たせすぎると、次にこのコードを編集する必要があるときに簡単に問題が発生する可能性があります。また、これはあまりスケーラブルなソリューションではありません。

私は通常、複数のスレッドからアクセスされないように保護しようとするすべてのリソースを、クラスのプライベート インスタンス変数にしてから、プライベート インスタンス変数としてロックも設定します。そうすれば、スケーリングが必要な場合に複数のオブジェクトをインスタンス化できます。

于 2008-09-29T10:08:44.267 に答える
0

これを行うより宣言的な方法は、アクセスを同期するメソッドでMethodImplOptions.Synchronized指定子を使用することです。

[MethodImpl(MethodImplOptions.Synchronized)] 
public void OneAtATime() { }

ただし、この方法はいくつかの理由でお勧めできません。そのほとんどは、ここここにあります。これを投稿するのは、あなたがそれを使いたがらないようにするためです. Java では、synchronizedはキーワードであるため、スレッド パターンを確認するときに出てくることがあります。

于 2008-10-01T11:55:10.953 に答える