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问题在于有两个队列,而不仅仅是一个,就绪队列中的线程将具有优先级,滥用这个概念使我能够编写一个失败的测试,但这并没有回答您的问题,而是重新发明了轮子,也许像这样重用代码是最容易/最快的,因为看起来您正在以保证的顺序重新生成计数信号量。