6

我有几个从 ClassA 继承的对象,它有一个抽象方法 MethodA。

这些继承对象中的每一个都可以允许最多特定数量的线程同时进入它们的 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

派生类型与静态信号量一起用作基类中的类型参数,以在每个子类的所有实例之间共享一个信号量。然后有一些混乱,以确保只有一种类型处于活动状态。快速测试表明这可以正常工作,但存在问题。

例如假设ClassA1当前正在执行的方法。如果执行此方法的新请求以很高的频率到达,则可能会发生其他派生类没有机会执行的情况,因为不断有新线程执行该类的方法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

假设您有所有相关实例的列表,此代码将锁定所有其他实例,因此在任何给定时间只允许执行一个实例的代码:

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 回答