【问题标题】:C# controlling threads (resume/suspend)C# 控制线程(恢复/挂起)
【发布时间】:2014-06-17 20:58:36
【问题描述】:

我正在尝试模拟(非常基本和简单的)操作系统进程管理器子系统,我有三个“进程”(工作人员)向控制台写入内容(这是一个示例):

    public class Message
    {
        public Message() { }
        public void Show()
        {
            while (true)
            {
                Console.WriteLine("Something");
                Thread.Sleep(100);
            }
        }
    }

每个工作人员都应该在不同的线程上运行。我现在就是这样做的: 我有一个 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);
            }
        }

问题在于它不能始终如一地工作。它甚至在这种状态下根本不起作用,我必须在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 而不是挂起/恢复,它可以工作,但由于该事件是共享的,因此依赖它的所有线程同时唤醒,而我一次只需要一个特定线程处于活动状态。

如果有人可以帮助我弄清楚如何正常暂停/恢复任务/线程,那就太好了。 我正在做的是试图模拟简单的抢先式多任务处理。 谢谢。

【问题讨论】:

  • 你不试试线程池吗
  • 另一种“干净”的方法究竟是什么?运行您的示例代码并获得预期的结果?这在一般情况下有用吗?
  • @DhavalPatel 我不确定如何在我的情况下具体使用它,乍一看似乎很复杂
  • 我建议您将类名从 Process 更改为其他名称,以避免与 Microsoft 的 Process 类产生歧义
  • 您可以使用asyncawait Task.Run() 等以及.Net 4.5 中内置的TAP 的其余部分,并将线程控制委托给框架。 msdn.microsoft.com/en-us/magazine/jj991977.aspx

标签: c# multithreading operating-system preemption


【解决方案1】:

Thread.Suspend 是邪恶的。它和Thread.Abort 一样邪恶。在任意、不可预测的位置暂停时,几乎没有代码是安全的。它可能持有一个锁,导致其他线程也暂停。您很快就会在系统的其他部分遇到死锁或不可预知的停顿。

假设您不小心暂停了string 的静态构造函数。现在所有想要使用string 的代码也都被停止了。 Regex 在内部使用锁定缓存。如果您在获取此锁时暂停所有Regex 相关代码可能会暂停。这只是两个令人震惊的例子。

很可能,在 Console 类的深处暂停一些代码会产生意想不到的后果。

我不知道该向您推荐什么。这似乎是一个学术练习,所以谢天谢地,这对你来说不是生产问题。用户模式的等待和取消在实践中必须是合作的。

【讨论】:

  • 你已经说得很清楚了,谢谢。我设法实现了静态 ManualResetEvent 变量,并在线程循环内部使用 WaitOne() 观察它。现在的问题是,我正在使用许多线程,并且所有线程的一个变量都不起作用。我不确定如何将对 ManualResetEvent 的引用传递给线程以跟踪它。
  • @Steel 考虑为每个您希望能够挂起的逻辑线程使用一个事件。您还可以使用 Barrier 类,该类允许控制器线程轻松确定线程已使用屏障签入并已协作挂起自身。
  • 不确定 Barrier,但 ManualResetEvent 的数组(其中进程由其唯一 PID 标识)起到了作用,尽管我认为这是快速而肮脏的事情。但据我所知,工作完美无缺。
【解决方案2】:

我设法使用带有 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);
            }
        }

【讨论】:

    【解决方案3】:

    关于Suspend()Resume(),我能给出的最佳建议是:不要使用它。你做错了™。

    每当你想使用Suspend()Resume() 对来控制你的线程时,你应该立即退后一步,问问自己,你在这里做什么。我理解,程序员倾向于将代码路径的执行视为必须控制的东西,就像一些需要永久命令和控制的愚蠢的僵尸工人一样。这可能与在学校和大学中学到的有关计算机的知识有关:计算机只执行您告诉它们的操作。

    女士们先生们,这里有个坏消息:如果你这样做,这就是所谓的“微观管理”,有些人甚至会称之为“控制狂思维”。

    相反,我强烈建议您以不同的方式思考它。试着把你的线程想象成智能实体,它们不会造成伤害,它们唯一想要的就是得到足够的工作。他们只需要一点指导,仅此而已。您可以在他们面前放置一个装满工作的容器(工作任务队列),并让他们在完成之前的任务后自己从该容器中提取任务。当容器为空时,所有任务都已处理完毕,无事可做,允许它们进入睡眠状态,WaitFor(alarm) 将在新任务到达时发出信号。

    因此,与其指挥和控制一群愚蠢的僵尸奴隶,如果不敲响他们身后的鞭子,他们就什么都做不了,而是故意引导一群聪明的同事,让它发生。这就是构建可扩展架构的方式。你不必是个控制狂,只要对自己的代码有一点信心。

    当然,与往常一样,该规则也有例外。但是没有那么多,我建议从工作假设开始,即您的代码可能是规则,而不是例外。

    【讨论】:

      猜你喜欢
      • 2013-07-20
      • 2011-07-09
      • 2012-05-20
      • 1970-01-01
      • 1970-01-01
      • 2013-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多