Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/296.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 一次只能在一个方法中执行所有线程?_C#_Multithreading_Semaphore_Locks - Fatal编程技术网

C# 一次只能在一个方法中执行所有线程?

C# 一次只能在一个方法中执行所有线程?,c#,multithreading,semaphore,locks,C#,Multithreading,Semaphore,Locks,我有几个对象继承自ClassA,它有一个抽象方法MethodA 这些继承对象中的每一个都允许最多特定数量的线程同时进入其MethodA catch:线程只能在对象的MethodA中,而不能同时处理其他对象的MethodA 我怎样才能解决这个问题?我正在考虑使用信号量,但不知道具体怎么做,因为我无法充分思考问题以获得解决方案 编辑: 示例代码(可能包含语法错误:) 假设您有一个所有相关实例的列表,此代码将锁定所有其他实例,因此在任何给定时间只允许执行一个实例的代码: void MethodA()

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

这些继承对象中的每一个都允许最多特定数量的线程同时进入其MethodA

catch:线程只能在对象的MethodA中,而不能同时处理其他对象的MethodA

我怎样才能解决这个问题?我正在考虑使用信号量,但不知道具体怎么做,因为我无法充分思考问题以获得解决方案

编辑:

示例代码(可能包含语法错误:)


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

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);
}   
}

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

例如,假设
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();
}
派生类将如下所示

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.
    }
}
内部密封等级ClassA1:ClassA
{
受保护的覆盖无效方法核心()
{
//在这里工作。
}
}
内部密封等级ClassA2:ClassA
{
受保护的覆盖无效方法核心()
{
//在这里工作。
}
}
不幸的是,我现在没有时间更详细地解释它是如何工作的以及为什么工作的,但我明天会更新答案

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而不跟踪它将允许新实例启动,而之前的一些实例正在完成

在开始和结束时,另一把钥匙周围的锁相当重要



还要注意的是,在这种设置中,一种类型可能会耗尽其他类型,因此如果这是一种竞争激烈的资源,则需要注意。

我建议您发布一些代码,让人们帮助您。这确实是一个非常模糊的问题。如果我理解正确,您希望启动多个线程来完成Methodo的工作dA,但只有在前一个线程完成之后?可以查看信号量类:在不同的派生类型上有几种方法
MethodA
ClassA2.MethodA
ClassA3.MethodA
等等。在任何时间点,线程都只允许执行其中一种方法。四个线程方法
ClassA2.MethodA
中的s是有效的,但不是一个线程执行
ClassA1.MethodA
和三个线程执行
ClassA2.MethodA
。这是一个非常奇怪的请求,因此可能是人为的请求。锁定需要围绕数据,而不是围绕方法。因此,这个问题的核心没有什么意义。这段代码可以您需要确保“锁定所有东西”逻辑上是原子的,这意味着在锁定集合中的所有项时添加一个单独的互斥锁。此外,
对象
集合将从何而来?他将如何获得其抽象类的所有实例?@Servy 1。我认为它不会死锁,因为锁总是在同一个ord中获得呃.2.他必须以某种方式创建这个列表,例如在构造时插入每个对象。我不能创建一个静态字段,例如在包含所有子类的所有实例的抽象类中创建一个静态字段吗?我当然可以在每次创建或销毁子类时通过基类构造函数填充和取消填充该列表期待整个过程:)(需要检查其他子类的信号量是否为空的部分,以便知道是否可以执行waitone())非常感谢完整版本,我打赌这需要一些工作。不过只有两个问题。派生类不应该调用MethodA而不是MethodACore吗?MethodA不应该是虚拟的吗?再次感谢,我今晚回家后会实施。是的,那花了一些时间D
ClassA.MethodA
执行大量锁定工作,并调用抽象(因此隐式虚拟)方法
ClassA.MethodACore
来执行实际工作。此方法反过来在派生类中被重写,以实现每个派生类的特定任务。请记住,这很容易被破坏。想象一下
MeanClass:ClassA
。泛型参数中的类型是实例本身的类型没有限制。当然,这是正确的。这整件事还远远不够完美。我真的没想到这个问题会变成这样。我坚信有比我更好的解决方案。整个过程不应该是尝试/最终确定
threadsInMethodA
Cleaner than my solution:+1该实现不允许同时执行同一派生类型的不同实例。它也不限制并发执行线程的最大数量。@DanielBrückner编辑;两者都不是特别重要的问题
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();
}