如何在一个线程中执行2个连续的命令而不发生上下文切换?
我有一个 C# 程序,它有一个 "Agent" 类。程序创建了多个Agent,每个Agent都有一个“run()”方法,该方法执行一个Task(即:Task.Factory.StartNew()...) .
每个Agent都会进行一些计算,然后需要等待所有其他Agent完成计算,然后才能进入下一阶段(他的行动将根据其他Agent的计算而定)。
为了让 Agent 等待,我创建了一个 CancellationTokenSource(名为“tokenSource”),并且为了提醒程序该 Agent 将要休眠,我抛出了一个事件。因此,这两个连续的命令是:(
(1) OnWaitingForAgents(new EventArgs());
(2) tokenSource.Token.WaitHandle.WaitOne();
事件由 "AgentManager" 类捕获,该类本身就是一个线程,第二个命令使代理任务线程休眠,直到收到信号取消令牌)。
每次触发上述事件时,AgentManager 类都会捕获该事件,并向计数器添加 +1。如果计数器的数量等于程序中使用的代理数量,则 AgentManager(保存对所有代理的引用)将按如下方式唤醒每个代理:
agent.TokenSource.Cancel();
现在我们遇到我的问题:第一个命令由代理异步执行,然后由于线程之间的上下文切换,AgentManager 似乎捕获了该事件,并继续唤醒所有代理。但是 - 当前特工甚至还没有达到第二个命令! 因此,特工正在接收“唤醒”信号,然后他才会去睡觉,这意味着他陷入沉睡之中,没有人叫醒他! 有没有办法将两个连续的方法“原子化”在一起,这样就不会发生上下文切换,从而迫使Agent在AgentManager有机会唤醒他之前进入睡眠状态?
I have a C# program, which has an "Agent" class. The program creates several Agents, and each Agent has a "run()" method, which executes a Task (i.e.: Task.Factory.StartNew()...).
Each Agent performs some calculations, and then needs to wait for all the other Agents to finish their calculations, before proceeding to the next stage (his actions will be based according to the calculations of the others).
In order to make an Agent wait, I have created a CancellationTokenSource (named "tokenSource"), and in order to alert the program that this Agent is going to sleep, I threw an event. Thus, the 2 consecutive commands are:
(1) OnWaitingForAgents(new EventArgs());
(2) tokenSource.Token.WaitHandle.WaitOne();
(The event is caught by an "AgentManager" class, which is a thread in itself, and the 2nd command makes the Agent Task thread sleep until a signal will be received for the Cancellation Token).
Each time the above event is fired, the AgentManager class catches it, and adds +1 to a counter. If the number of the counter equals the number of Agents used in the program, the AgentManager (which holds a reference to all Agents) wakes each one up as follows:
agent.TokenSource.Cancel();
Now we reach my problem: The 1st command is executed asynchronously by an Agent, then due to a context switch between threads, the AgentManager seems to catch the event, and goes on to wake up all the Agents. BUT - the current Agent has not even reached the 2nd command yet !
Thus, the Agent is receiving a "wake up" signal, and only then does he go to sleep, which means he gets stuck sleeping with no one to wake him up!
Is there a way to "atomize" the 2 consecutive methods together, so no context switch will happen, thus forcing the Agent to go to sleep before the AgentManager has the chance to wake him up?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您所询问的低级技术是线程同步。您拥有的是关键部分(或关键部分的一部分),您需要保护对其的访问。我很惊讶您已经了解了多线程编程,但尚未了解线程同步和临界区!对于任何类型的“低级”多线程编程来说,了解这些事情都是至关重要的。
The low-level technique that you are asking about is thread synchronisation. What you have there is a critical section (or part of one), and you need to protect access to it. I'm surprised that you've learned about multithreaded programming without having learned about thread synchronisation and critical sections yet! It's essential to know about these things for any kind of "low-level" multithreaded programming.
也许可以看看 .NET 4 中的 Parallel.Invoke 或 Parallel.For,它允许您并行执行方法并等待所有并行方法被调用。
http://msdn.microsoft.com/en-us/library/dd992634.aspx
看起来这会对你有很大帮助,并为你处理所有的排队问题。
Maybe look into Parallel.Invoke or Parallel.For in .NET 4, which allows you to execute methods in parallel and wait until all parallel methods have been invoked.
http://msdn.microsoft.com/en-us/library/dd992634.aspx
Seems like that would help you out a lot, and take care of all the queuing for you.
嗯...我不认为在 .NET 中开发软件而担心上下文切换是个好主意(甚至不可能),因为 Windows 或 .NET 都不是实时的。可能您在该代码中遇到了另一种问题。
据我了解,您只需并行运行所有代理,并且您希望等到所有代理都完成后才能进入下一阶段。您可以使用多种技术来实现此目的,最简单的一种是使用
Monitor.Wait(对象监视器)
和Monitor.PulseAll(对象监视器)
。在任务库中也有几件事可以做。正如 @jishi 所指出的,您可以使用
Parallel
风格,或者生成大量Task
,然后使用Task.WaitAll(Task) 等待所有任务[]任务)
方法。您如何向该计数器加 1?您如何读取它?您应该使用 Interloked.Increment 来确保原子操作,并在易失性操作中读取它,例如使用 Thread.VolatileRead ,或者简单地将其放在锁定语句中。
humm... I don't think it's good idea (or even possible) develop software in .NET worrying about context switches, since neither Windows or .NET are real time. Probably you have another kind of problem in that code.
I've understood that you simply run all your agents in parallel, and you want to wait till all of them have finished to go to the next stage. You can use several techniques to accomplish that, the easiest one would be using
Monitor.Wait(Object monitor)
andMonitor.PulseAll(Object monitor)
.In the task library there are several things to do it as well. As @jishi has pointed out, you can use the
Parallel
flavours, or spawn a lot ofTask
s and then wait for all with theTask.WaitAll(Task[] tasks)
method.How are you adding 1 to that counter and how are you reading it? You should use
Interloked.Increment
to ensure an atomic operation, and read it in a volatile operation withThread.VolatileRead
for example, or simply put it in a lock statement.