C# 如何使用Monitor.Pulse将线程脉冲发送到就绪队列的前面而不是后面?

C# 如何使用Monitor.Pulse将线程脉冲发送到就绪队列的前面而不是后面?,c#,multithreading,thread-synchronization,C#,Multithreading,Thread Synchronization,我正在为我的应用程序实现一个资源管理器类,它应该处理多个线程 此管理器的要求是,如果管理器发现应用程序中已存在最大数量的实例,则将为其提供一个类和允许其创建的该类实例的限制,线程将调用请求和返回方法,以便从管理器中提供和获取实例,我希望它阻止请求线程,直到另一个线程返回资源的实例,因此代码: public ResourceManager<T> where T : new(){ private object _locker = new object(); publi

我正在为我的应用程序实现一个资源管理器类,它应该处理多个线程

此管理器的要求是,如果管理器发现应用程序中已存在最大数量的实例,则将为其提供一个类和允许其创建的该类实例的限制,线程将调用请求和返回方法,以便从管理器中提供和获取实例,我希望它阻止请求线程,直到另一个线程返回资源的实例,因此代码:

public ResourceManager<T> where T : new(){

    private object _locker = new object();

    public T RequestResource(){

        lock (_locker){
            while (!IsAnyAvailable()){
                Monitor.Wait(_locker);
            }

            return GetResource();
        }
    }

    public void ReturnResource(T instance){

        lock (_locker){
            ReturnResourceLogic(instance);
            Monitor.Pulse(_locker);
        }       

    }
}
虽然这段代码对我有用,但在阅读了线程同步后,我意识到由于等待队列中的线程位于就绪队列的后面,而不是前面,一种假设的情况是,多个线程不断请求资源,这可能会使一个不走运的线程饿死,而该线程恰好在准备队列和等待队列之间来回移动,而其他线程在等待队列中切断了队列

如何更改此代码以使此资源管理器公平并尊重来自线程的请求顺序

编辑:这里有一个失败的测试,它创建了一个假设的情况,即线程1或线程2总是会饿死

[TestClass]
public class UnitTest2
{
    private class ResourceManager<T> where T : new()
    {
        private int n=0;
        private object _locker = new object();

        public T RequestResource()
        {

            lock (_locker)
            {
                while (!IsAnyAvailable())
                {
                    Monitor.Wait(_locker);
                }

                return GetResource();
            }
        }

        private T GetResource()
        {
            n++;
            return new T();
        }

        private bool IsAnyAvailable()
        {
            return n < 1;
        }

        public void ReturnResource(T instance)
        {
            lock (_locker)
            {
                ReturnResourceLogic(instance);
                Monitor.Pulse(_locker);
            }

        }

        private void ReturnResourceLogic(T instance)
        {
            //Simulates some work
            Thread.Sleep(500);
            n--;
        }
    }

    class DummyClass { }

    [TestMethod]
    public void TestMethod1()
    {
        bool fair1 = false;
        bool fair2 = false;
        bool fair3 = false;
        var manager = new ResourceManager<DummyClass>();

        Stopwatch watch = new Stopwatch();

        //string builder for debugging purposes
        StringBuilder builder = new StringBuilder();

        Thread th1 = new Thread(() =>
        {
            while (true)
            {
                builder.AppendLine(watch.Elapsed + " 1 requesting");
                var temp = manager.RequestResource();
                builder.AppendLine(watch.Elapsed + " 1 got resource");
                fair1 = true;
                Thread.Sleep(500);
                builder.AppendLine(watch.Elapsed + " 1 returning");
                manager.ReturnResource(temp);
                builder.AppendLine(watch.Elapsed + " 1 returned");
                Thread.Sleep(750);
            }
        });

        Thread th2 = new Thread(() =>
        {
            while (true)
            {
                builder.AppendLine(watch.Elapsed + " 2 requesting");
                var temp = manager.RequestResource();
                builder.AppendLine(watch.Elapsed + " 2 got resource");
                fair2 = true;
                Thread.Sleep(500);
                builder.AppendLine(watch.Elapsed + " 2 returning");
                manager.ReturnResource(temp);
                builder.AppendLine(watch.Elapsed + " 2 returned");
                Thread.Sleep(750);
            }
        });

        Thread th3 = new Thread(() =>
        {
            Thread.Sleep(750);
            while (true)
            {
                builder.AppendLine(watch.Elapsed + " 3 requesting");
                var temp = manager.RequestResource();
                builder.AppendLine(watch.Elapsed + " 3 got resource");
                fair3 = true;
                Thread.Sleep(500);
                builder.AppendLine(watch.Elapsed + " 3 returning");
                manager.ReturnResource(temp);
                builder.AppendLine(watch.Elapsed + " 3 returned");
                Thread.Sleep(750);
            }
        });

        th1.Name = "Thread1";
        th2.Name = "Thread2";
        th3.Name = "Thread3";

        watch.Start();
        th1.Start();
        th2.Start();
        th3.Start();

        Thread.Sleep(60000);

        var result = builder.ToString();

        Assert.IsTrue(fair1);
        Assert.IsTrue(fair2);
        Assert.IsTrue(fair3);
    }
}

您的问题没有什么意义,您的工作假设是等待的线程没有等待_locker监视器,因此当您脉冲时,它们实际上不会做任何有用的事情。事实并非如此,他们首先进入等待队列的唯一方式是等待监视器。排队是为了公平。你的问题是虚构的,不要更改代码。你的问题是虚构的。好的,现在告诉失败的测试。队列是公平的。螺纹不能直线切割,所有螺纹都将被加工。请看@EliAlgranti问题在于有两个队列,而不仅仅是一个,就绪队列中的线程将具有优先级,滥用这个概念使我能够编写一个失败的测试,但这并没有回答您的问题,而是重新发明了轮子,也许像这样重用代码是最容易/最快的,因为看起来您正在以保证的顺序重新生成计数信号量。