我正在尝试模拟(非常基本和简单)OS进程管理器子系统,我有三个“进程”(工作者)向控制台写一些东西(这是一个例子):
public class Message
{
public Message() { }
public void Show()
{
while (true)
{
Console.WriteLine("Something");
Thread.Sleep(100);
}
}
}
每个worker应该在不同的线程上运行。这就是我现在这样做的方式: 我有一个Process类,构造函数接受Action委托并从中启动一个线程并挂起它。
public class Process
{
Thread thrd;
Action act;
public Process(Action act)
{
this.act = act;
thrd = new Thread(new ThreadStart(this.act));
thrd.Start();
thrd.Suspend();
}
public void Suspend()
{
thrd.Suspend();
}
public void Resume()
{
thrd.Resume();
}
}
在该状态下,它会在我的调度程序恢复它之前等待,给它一个时间片来运行,然后再次暂停它。
public void Scheduler()
{
while (true)
{
//ProcessQueue is just FIFO queue for processes
//MainQueue is FIFO queue for ProcessQueue's
ProcessQueue currentQueue = mainQueue.Dequeue();
int count = currentQueue.Count;
if (currentQueue.Count > 0)
{
while (count > 0)
{
Process currentProcess = currentQueue.GetNext();
currentProcess.Resume();
//this is the time slice given to the process
Thread.Sleep(1000);
currentProcess.Suspend();
Console.WriteLine();
currentQueue.Add(currentProcess);
count--;
}
}
mainQueue.Enqueue(currentQueue);
}
}
问题在于它不能始终如一地工作。它甚至在这种状态下根本不起作用,我必须在工作者的Show()方法中的WriteLine之前添加Thread.Sleep(),就像这样。
public void Show()
{
while (true)
{
Thread.Sleep(100); //Without this line code doesn't work
Console.WriteLine("Something");
Thread.Sleep(100);
}
}
我一直在尝试使用ManualResetEvent而不是suspend / resume,它可以工作,但是由于该事件是共享的,所以依赖它的所有线程同时唤醒,而我只需要一个特定的线程一次处于活动状态。
如果有人可以帮我弄清楚如何正常暂停/恢复任务/线程,那就太好了。 我正在做的是尝试模拟简单的抢占式多任务处理。 谢谢。
答案 0 :(得分:4)
Thread.Suspend
是邪恶的。它和Thread.Abort
一样邪恶。在任意,不可预测的位置暂停时,几乎没有代码是安全的。它可能会持有一个锁,导致其他线程也会暂停。您很快就会在系统的其他部分遇到死锁或不可预测的停顿。
想象一下,你不小心暂停了string
的静态构造函数。现在,所有想要使用string
的代码也会停止。 Regex
内部使用锁定的缓存。如果在执行此锁定时暂停,则所有Regex
相关代码可能会暂停。这些只是两个令人震惊的例子。
将某些代码暂停在Console
类的深处可能会产生意想不到的后果。
我不确定该向您推荐什么。这似乎是一个学术练习,所以谢天谢地,这不是一个生产问题。用户模式等待和取消必须在实践中合作。
答案 1 :(得分:0)
我设法使用带有ManualResetEvent数组的静态类来解决这个问题,其中每个进程都由它的唯一ID标识。但我认为这样做很脏。我愿意采取其他方式来实现这一目标。 UPD:添加锁以保证线程安全
public sealed class ControlEvent
{
private static ManualResetEvent[] control = new ManualResetEvent[100];
private static readonly object _locker = new object();
private ControlEvent() { }
public static object Locker
{
get
{
return _locker;
}
}
public static void Set(int PID)
{
control[PID].Set();
}
public static void Reset(int PID)
{
control[PID].Reset();
}
public static ManualResetEvent Init(int PID)
{
control[PID] = new ManualResetEvent(false);
return control[PID];
}
}
在工人阶级
public class RandomNumber
{
static Random R = new Random();
ManualResetEvent evt;
public ManualResetEvent Event
{
get
{
return evt;
}
set
{
evt = value;
}
}
public void Show()
{
while (true)
{
evt.WaitOne();
lock (ControlEvent.Locker)
{
Console.WriteLine("Random number: " + R.Next(1000));
}
Thread.Sleep(100);
}
}
}
在流程创建活动
RandomNumber R = new RandomNumber();
Process proc = new Process(new Action(R.Show));
R.Event = ControlEvent.Init(proc.PID);
最后,在调度程序
中public void Scheduler()
{
while (true)
{
ProcessQueue currentQueue = mainQueue.Dequeue();
int count = currentQueue.Count;
if (currentQueue.Count > 0)
{
while (count > 0)
{
Process currentProcess = currentQueue.GetNext();
//this wakes the thread
ControlEvent.Set(currentProcess.PID);
Thread.Sleep(quant);
//this makes it wait again
ControlEvent.Reset(currentProcess.PID);
currentQueue.Add(currentProcess);
count--;
}
}
mainQueue.Enqueue(currentQueue);
}
}
答案 2 :(得分:0)
关于Suspend()
和Resume()
,我可以给出最好的建议:不要使用它。你做错了™。
每当你感到有诱惑使用Suspend()
和Resume()
对来控制你的线程时,你应该立即退后一步,问问自己,你在这里做了什么。我理解,程序员倾向于将代码路径的执行视为必须控制的东西,就像一些需要永久命令和控制的愚蠢的僵尸工作者。这可能是学到的关于学校和大学计算机的东西的函数:计算机只做你告诉他们的事情。
女士&先生们,这是坏消息:如果你这样做,这就是所谓的“微观管理”,甚至有人称之为“控制狂”。
相反,我会强烈地鼓励你以不同的方式思考它。试着将你的线程视为智能实体,没有任何伤害,他们唯一需要的是获得足够的工作。他们只是需要一点指导,就是这样。您可以在其前面放置一个装满工作的容器(工作任务队列),并让他们在完成上一个任务后立即从该容器中拉出任务。当容器为空时,所有任务都会被处理,没有什么可做的,他们可以入睡,WaitFor(alarm)
每当新任务到达时都会发出信号。
因此,你不是指挥和控制一群愚蠢的僵尸奴隶,如果你不打破他们背后的鞭子就无法做任何事情,你会故意引导一群聪明的同事并让它发生。这就是构建可扩展架构的方式。你不必成为一个控制狂,只要对你自己的代码有一点信心。
当然,与往常一样,该规则也有例外。但是没有那么多,我建议从工作假设开始,你的代码可能是规则,而不是例外。