C#控制线程(恢复/挂起)
我试图模拟(非常基本和简单的)OS process manager子系统,我有三个“进程”(工作者)在控制台上写东西(这是一个示例): 每个工人都应该在不同的线程上运行。我现在就是这样做的: 我有一个Process类,构造函数接受Action委托并从中启动一个线程,然后将其挂起C#控制线程(恢复/挂起),c#,multithreading,operating-system,preemption,C#,Multithreading,Operating System,Preemption,我试图模拟(非常基本和简单的)OS process manager子系统,我有三个“进程”(工作者)在控制台上写东西(这是一个示例): 每个工人都应该在不同的线程上运行。我现在就是这样做的: 我有一个Process类,构造函数接受Action委托并从中启动一个线程,然后将其挂起 public class Process { Thread thrd; Action act; public Process(Action act) { this.act
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);
}
}
问题是它不能始终如一地工作。在这种状态下它甚至根本不工作,我必须在worker的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,它可以工作,但由于该事件是共享的,所有依赖它的线程都会同时唤醒,而我一次只需要一个特定的线程处于活动状态
如果有人能帮我弄清楚如何正常暂停/恢复任务/线程,那就太好了。
我正在做的是尝试模拟简单的先发制人多任务处理。
谢谢
Thread.Suspend
是邪恶的。它几乎和线程一样邪恶。中止。在任意、不可预测的位置暂停时,几乎没有代码是安全的。它可能持有导致其他线程暂停的锁。您很快就会在系统的其他部分遇到死锁或不可预知的暂停
假设您意外地暂停了string
的静态构造函数。现在,所有想要使用字符串的代码也都被暂停Regex
在内部使用锁定的缓存。如果在使用此锁时暂停,所有Regex
相关代码都可能暂停。这只是两个令人震惊的例子
很可能,在控制台
类的深处挂起一些代码会产生意想不到的后果
我不知道该向你推荐什么。这似乎是一个学术练习,所以谢天谢地,这对您来说不是生产问题。用户模式的等待和取消在实践中必须是合作的。线程。挂起是邪恶的。它几乎和线程一样邪恶。中止。在任意、不可预测的位置暂停时,几乎没有代码是安全的。它可能持有导致其他线程暂停的锁。您很快就会在系统的其他部分遇到死锁或不可预知的暂停
假设您意外地暂停了string
的静态构造函数。现在,所有想要使用字符串的代码也都被暂停Regex
在内部使用锁定的缓存。如果在使用此锁时暂停,所有Regex
相关代码都可能暂停。这只是两个令人震惊的例子
很可能,在控制台
类的深处挂起一些代码会产生意想不到的后果
我不知道该向你推荐什么。这似乎是一个学术练习,所以谢天谢地,这对您来说不是生产问题。用户模式的等待和取消在实践中必须是合作的。我使用带有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);
}
}
我使用带有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);
}
}
关于Suspend()
和Resume()
,我能给出的最好建议是:不要使用它。你做错了™.
每当你想使用Suspend()
和Resume()
对来控制线程时,你应该立即后退一步,问问自己,你在这里做什么。我理解,程序员倾向于认为代码路径的执行是必须控制的,比如一些愚蠢的僵尸工人需要永久命令和控制。这可能是学校和大学里学过的关于计算机的知识的一个作用:计算机只做你们告诉他们的事情
女士们先生们,坏消息是:如果你这样做,这被称为“微观管理”,有些人甚至会称之为“控制怪胎思维”
相反,我强烈建议你用另一种方式来思考这个问题。试着把你的线程想象成智能实体,它们不会造成伤害,它们唯一想要的就是得到足够的工作。他们只需要一点指导,仅此而已。您可以在他们前面放置一个装满工作的容器(工作任务队列),并让他们在完成上一个任务后立即从该容器中取出任务。当容器为空时,所有任务都已处理,无事可做,允许它们进入睡眠状态,并等待(报警)
,新任务到达时将发出信号
因此,你不是指挥和控制一群愚蠢的僵尸奴隶,如果你不鞭策他们,他们就不能做任何正确的事情,而是故意引导一个聪明的同事团队,让它发生。这就是可伸缩体系结构的构建方式。你不必是一个控制狂,只要对自己的代码有一点信心
当然,一如既往,这一规则也有例外。但是没有那么多,我建议从工作假设开始,您的代码可能是规则,而不是例外。我能提供的最好建议