一次只能在一个方法中使用所有线程
本文关键字:线程 方法 一次 一个 | 更新日期: 2023-09-27 17:51:17
我有几个从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...
派生类型与静态信号量一起用作基类中的类型参数,以在每个子类的所有实例之间共享一个信号量。然后会出现一些混乱,以确保只有一种类型是活动的。快速测试表明此操作正常,但存在问题。
例如,假设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.
}
}
不幸的是,我现在没有时间更详细地解释这是如何以及为什么工作的,但我明天会更新答案。
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,则会在前几个实例完成时启动新实例。
在开始和结束处围绕另一个键的锁是相当重要的。
还要注意,使用这种设置,一种类型可能会饿死其他类型,因此,如果这是一个严重争用的资源,则需要注意。
假设您有一个所有相关实例的列表,此代码将锁定所有其他实例,从而在任何给定时间只允许执行一个实例的代码:
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);
}
}