C# 如何在多线程应用程序中实现顺序阻塞行为
C# how to achive sequential blocking behavior in multithread application
我正在编写一个应模拟 PLC 行为的应用程序。这意味着我必须 运行 多个线程以确保一次只有一个线程处于活动状态,而所有其他线程都被挂起。
例如:
- 线程 1 每 130 毫秒重复一次并阻塞所有其他线程。有效运行时间为30ms,线程重启前剩余的100ms可以被其他线程使用。
- 线程2每300ms重复一次,阻塞除线程1以外的所有线程。有效运行时间为50ms(剩余的250ms可以被其他线程使用)。线程 2 暂停,直到线程 1 完成执行代码(线程 1 剩余的 100 毫秒),一旦线程 1 休眠,它从暂停的地方恢复
- 线程 3 每 1000 毫秒重复一次。有效运行时间为100ms。只有当所有其他线程都挂起时,该线程才会继续执行。
最高优先级是在任务被再次调用之前完成,否则我必须做出反应,所以应该阻塞的线程不要运行到某个点,否则多核处理会详细说明代码,只等待通过结果。
我读了几篇文章,了解到 Thread.suspend 不被推荐,信号量或监视器操作意味着代码一直执行到代码中的特定固定点,而我必须暂停线程恰好在当另一个线程(具有更高 "priority")被调用时,执行已经到来。
我也查看了优先级设置,但它似乎不是 100% 相关,因为系统可以覆盖优先级。
是否有正确或至少可靠的方法来编写阻塞机制?
希望我不会误解你的问题:)
您的问题的一种可能是使用并发队列:https://msdn.microsoft.com/de-de/library/dd267265(v=vs.110).aspx
例如,您创建一个枚举来控制您的状态并初始化队列:
private ConcurrentQueue<Action> _clientActions ;
private enum Statuskatalog
{
Idle,
Busy
};
创建一个计时器以启动并创建一个 timerfunktion。
Timer _taskTimer = new Timer(ProcessPendingTasks, null, 100, 333);
private void ProcessPendingTasks(object x)
{
_status = Statuskatalog.Busy;
_taskTimer.Change(Timeout.Infinite, Timeout.Infinite);
Action currentTask;
while( _clientActions.TryDequeue( out currentTask ))
{
var task = new Task(currentTask);
task.Start();
task.Wait();
}
_status=Statuskatalog.Idle;
}
现在您只需将您的任务作为委托添加到队列中:
_clientActions.Enqueue(delegate { **Your task** });
if (_status == Statuskatalog.Idle) _taskTimer.Change(0, 333);
在此基础上,您可以管理您提出的特殊要求。
希望这是您要搜索的内容。
我认为您根本不需要为 Thread
增加负担。相反,您可以将 Task
与优先级 TaskScheduler
一起使用(编写或通过谷歌搜索并不难找到)。
这使得代码很容易编写,例如最高优先级的线程可能是这样的:
while (!cancellationRequested)
{
var repeatTask = Task.Delay(130);
// Do your high priority work
await repeatTask;
}
您的其他任务将具有类似的基本布局,但它们在任务调度程序中的优先级较低(这通常由任务调度程序处理,每个任务优先级都有一个单独的队列)。偶尔,他们可以检查是否有更高优先级的任务,如果有,他们可以做await Task.Yield();
。事实上,在你的情况下,你似乎甚至不需要真正的队列——这让这变得更容易,甚至更好,让你真正有效地使用 Task.Yield
。
最终结果是所有三个周期性任务都在单个线程上高效 运行(如果它们都在等待,甚至根本没有线程)。
当然,这确实依赖于协作式多任务处理。像 Windows 上的抢占一样处理完全实时是不可能的 - 部分抢占解决方案往往充满问题。如果您可以控制任务中花费的大部分时间(并使用异步 I/O 卸载任何其他工作),合作解决方案实际上效率更高,并且可以减少延迟(尽管它真的应该没什么大不了的)。
我正在编写一个应模拟 PLC 行为的应用程序。这意味着我必须 运行 多个线程以确保一次只有一个线程处于活动状态,而所有其他线程都被挂起。 例如:
- 线程 1 每 130 毫秒重复一次并阻塞所有其他线程。有效运行时间为30ms,线程重启前剩余的100ms可以被其他线程使用。
- 线程2每300ms重复一次,阻塞除线程1以外的所有线程。有效运行时间为50ms(剩余的250ms可以被其他线程使用)。线程 2 暂停,直到线程 1 完成执行代码(线程 1 剩余的 100 毫秒),一旦线程 1 休眠,它从暂停的地方恢复
- 线程 3 每 1000 毫秒重复一次。有效运行时间为100ms。只有当所有其他线程都挂起时,该线程才会继续执行。
最高优先级是在任务被再次调用之前完成,否则我必须做出反应,所以应该阻塞的线程不要运行到某个点,否则多核处理会详细说明代码,只等待通过结果。
我读了几篇文章,了解到 Thread.suspend 不被推荐,信号量或监视器操作意味着代码一直执行到代码中的特定固定点,而我必须暂停线程恰好在当另一个线程(具有更高 "priority")被调用时,执行已经到来。
我也查看了优先级设置,但它似乎不是 100% 相关,因为系统可以覆盖优先级。
是否有正确或至少可靠的方法来编写阻塞机制?
希望我不会误解你的问题:)
您的问题的一种可能是使用并发队列:https://msdn.microsoft.com/de-de/library/dd267265(v=vs.110).aspx
例如,您创建一个枚举来控制您的状态并初始化队列:
private ConcurrentQueue<Action> _clientActions ;
private enum Statuskatalog
{
Idle,
Busy
};
创建一个计时器以启动并创建一个 timerfunktion。
Timer _taskTimer = new Timer(ProcessPendingTasks, null, 100, 333);
private void ProcessPendingTasks(object x)
{
_status = Statuskatalog.Busy;
_taskTimer.Change(Timeout.Infinite, Timeout.Infinite);
Action currentTask;
while( _clientActions.TryDequeue( out currentTask ))
{
var task = new Task(currentTask);
task.Start();
task.Wait();
}
_status=Statuskatalog.Idle;
}
现在您只需将您的任务作为委托添加到队列中:
_clientActions.Enqueue(delegate { **Your task** });
if (_status == Statuskatalog.Idle) _taskTimer.Change(0, 333);
在此基础上,您可以管理您提出的特殊要求。
希望这是您要搜索的内容。
我认为您根本不需要为 Thread
增加负担。相反,您可以将 Task
与优先级 TaskScheduler
一起使用(编写或通过谷歌搜索并不难找到)。
这使得代码很容易编写,例如最高优先级的线程可能是这样的:
while (!cancellationRequested)
{
var repeatTask = Task.Delay(130);
// Do your high priority work
await repeatTask;
}
您的其他任务将具有类似的基本布局,但它们在任务调度程序中的优先级较低(这通常由任务调度程序处理,每个任务优先级都有一个单独的队列)。偶尔,他们可以检查是否有更高优先级的任务,如果有,他们可以做await Task.Yield();
。事实上,在你的情况下,你似乎甚至不需要真正的队列——这让这变得更容易,甚至更好,让你真正有效地使用 Task.Yield
。
最终结果是所有三个周期性任务都在单个线程上高效 运行(如果它们都在等待,甚至根本没有线程)。
当然,这确实依赖于协作式多任务处理。像 Windows 上的抢占一样处理完全实时是不可能的 - 部分抢占解决方案往往充满问题。如果您可以控制任务中花费的大部分时间(并使用异步 I/O 卸载任何其他工作),合作解决方案实际上效率更高,并且可以减少延迟(尽管它真的应该没什么大不了的)。