6

抽象メソッド MethodA を持つ ClassA から継承するオブジェクトがいくつかあります。

これらの継承オブジェクトのそれぞれは、特定の数までのスレッドを同時に MethodA に入れることができます。

問題点: スレッドはオブジェクトの MethodA にしか存在できませんが、他のオブジェクトの MethodA は同時に処理されません。

どうすればこれを解決できますか? セマフォの使用を考えていますが、解決策を得るのに十分な問題に頭を悩ませることができないため、その方法が正確にはわかりません。

編集:

サンプルコード (構文エラーが含まれている可能性があります:)

public class ClassA
{
  public virtual void MethodA{}
}

public class OneOfMySubclassesOfClassA // there can be multiple instances of each subclass!
{
  public override void MethodA{

  // WHILE any number of threads are in here, THIS MethodA shall be the ONLY MethodA in the entire program to contain threads
  EDIT2: // I mean: ...ONLY MethodA of a subclass (not of a instance of a subclass) in the entire program...
}
}

...and more subclasses...
4

3 に答える 3

2

派生型は、基本クラスの型引数として静的セマフォと共に使用され、各サブクラスのすべてのインスタンス間で共有される 1 つのセマフォを取得します。そして、アクティブなタイプが 1 つだけであることを確認するための混乱があります。簡単なテストでは、これが正しく機能することを示していますが、問題があります。

たとえば、 のメソッドClassA1が現在実行中であるとします。このメソッドを実行するための新しいリクエストが頻繁に到着すると、 class のメソッドを実行する新しいスレッドが常に存在するため、他の派生クラスが実行される機会がなくなる可能性がありますClassA1

internal abstract class ClassA<TDerived> : ClassA
{
    private const Int32 MaximumNumberConcurrentThreads = 3;

    private static readonly Semaphore semaphore = new Semaphore(ClassA<TDerived>.MaximumNumberConcurrentThreads, ClassA<TDerived>.MaximumNumberConcurrentThreads);

    internal void MethodA()
    {
        lock (ClassA.setCurrentlyExcutingTypeLock)
        {
            while (!((ClassA.currentlyExcutingType == null) || (ClassA.currentlyExcutingType == typeof(TDerived))))
            {
                Monitor.Wait(ClassA.setCurrentlyExcutingTypeLock);
            }

            if (ClassA.currentlyExcutingType == null)
            {
                ClassA.currentlyExcutingType = typeof(TDerived);
            }

            ClassA.numberCurrentlyPossiblyExecutingThreads++;

            Monitor.PulseAll(ClassA.setCurrentlyExcutingTypeLock);
        }

        try
        {
            ClassA<TDerived>.semaphore.WaitOne();

            this.MethodACore();
        }
        finally
        {
            ClassA<TDerived>.semaphore.Release();
        }

        lock (ClassA.setCurrentlyExcutingTypeLock)
        {
            ClassA.numberCurrentlyPossiblyExecutingThreads--;

            if (ClassA.numberCurrentlyPossiblyExecutingThreads == 0)
            {
                ClassA.currentlyExcutingType = null;

                Monitor.Pulse(ClassA.setCurrentlyExcutingTypeLock);
            }
        }
    }

    protected abstract void MethodACore();
}

で実際の実装を呼び出すには、ラッパー メソッドが使用されることに注意してくださいMethodACore。すべての派生クラス間で共有されるすべての同期オブジェクトは、非ジェネリック基本クラスにあります。

internal abstract class ClassA
{
    protected static Type currentlyExcutingType = null;

    protected static readonly Object setCurrentlyExcutingTypeLock = new Object();

    protected static Int32 numberCurrentlyPossiblyExecutingThreads = 0;
}

派生クラスは次のようになります。

internal sealed class ClassA1 : ClassA<ClassA1>
{
    protected override void MethodACore()
    {
        // Do work here.
    }
}

internal sealed class ClassA2 : ClassA<ClassA2>
{
    protected override void MethodACore()
    {
        // Do work here.
    }
}

残念ながら、これがどのように、そしてなぜ機能するのかを今すぐ説明する時間はありませんが、明日答えを更新します.

于 2013-01-11T15:24:16.063 に答える
1
public abstract class Foo
{
    private static Type lockedType = null;
    private static object key = new object();
    private static ManualResetEvent signal = new ManualResetEvent(false);
    private static int threadsInMethodA = 0;
    private static Semaphore semaphore = new Semaphore(5, 5);//TODO set appropriate number of instances

    public void MethodA()
    {
        lock (key)
        {
            while (lockedType != this.GetType())
            {
                if (lockedType == null)
                {
                    lockedType = this.GetType();
                    //there may be other threads trying to get into the instance we just locked in
                    signal.Set();
                }
                else if (lockedType != this.GetType())
                {
                    signal.WaitOne();
                }
            }

            Interlocked.Increment(ref threadsInMethodA);
        }
        semaphore.WaitOne();

        try
        {
            MethodAImplementation();
        }
        finally
        {
            lock (key)
            {
                semaphore.Release();
                int threads = Interlocked.Decrement(ref threadsInMethodA);
                if (threads == 0)
                {
                    lockedType = null;
                    signal.Reset();
                }
            }
        }
    }

    protected abstract void MethodAImplementation();
}

したがって、ここにはいくつかの重要なポイントがあります。まず、スレッドを持つことが許可されている唯一のインスタンスを表す静的オブジェクトがあります。 null来る次のスレッドが「それらの」インスタンスに入れることができることを意味します。別のインスタンスが「アクティブ」な場合、現在のスレッドは、ロックされたインスタンスがなくなるか、ロックされたインスタンスがそのスレッドのインスタンスである可能性があるものに変更されるまで、手動リセットイベントを待機します。

また、メソッド内のスレッドの数をカウントして、ロックされたインスタンスをいつ設定するかを知ることも重要ですnull(これを追跡せずにnullに設定すると、前のインスタンスのいくつかが終了している間に新しいインスタンスが開始されます。

開始時と終了時の別のキーの周りのロックはかなり重要です。

また、この設定では、あるタイプが他のタイプを枯渇させる可能性があることに注意してください。したがって、これが非常に競合するリソースである場合は、注意が必要です。

于 2013-01-11T15:30:15.867 に答える
0

関連するすべてのインスタンスのリストがあるとすると、このコードは他のすべてのインスタンスをロックするため、常に1つのインスタンスのコードのみを実行できます。

void MethodA()
{
     foreach (var obj in objects)
         if (obj != this)
             Monitor.Enter(obj);

try
{
    // do stuff
}
finally
{
        foreach( var obj in objects)
        if (obj != this)
                 Monitor.Exit(obj);
}   
}
于 2013-01-11T15:06:13.713 に答える