在 c# 中,我们是不是可以控制在单个处理器或一定数量的处理器上显式运行代码?

Posted

技术标签:

【中文标题】在 c# 中,我们是不是可以控制在单个处理器或一定数量的处理器上显式运行代码?【英文标题】:In c# do we have control to explicitly run code on single processor or a certain number of processors?在 c# 中,我们是否可以控制在单个处理器或一定数量的处理器上显式运行代码? 【发布时间】:2021-10-06 19:55:12 【问题描述】:

来自我们拥有的python背景:

    多处理 - 在处理器上并行运行多个任务 - 非常适合 CPU 密集型任务。

    多线程 - 通过单个处理器中的线程并行运行多个任务。非常适合 io 绑定任务。 IO 绑定只是因为 GIL 的存在导致在任何时间点只有 1 个线程能够使用处理器。

    异步等待 - 为 io 绑定任务进行协作编程。

使用python代码我可以实现上述任何一个。

在 c# 的情况下,我了解到: 我们可以显式启动线程,也可以使用 async await 和 Task.Run 的概念来隐式处理线程的创建和管理。

异步等待适用于 io 绑定任务。 Async await + Task.Run 适用于 cpu 绑定任务。

在 windows 窗体应用程序中,我们有同步上下文的概念,它确保只有单个 Ui 线程运行所有代码。

但是,如果我们使用 async await + Task.Run(这对 cpu 绑定的任务很有用),那么 Task.Run 中的代码将在单独的线程中运行。

await 下方的任何一种方式代码都将在 ui 线程上运行。

在控制台应用程序中,无论我们使用 async await 还是 async await + Task.Run,​​由于缺少同步上下文,await 之后的代码将由多个线程(来自线程池)并行运行。此处显示了一个示例: In console app, why does synchronous blocking code (Thread.Sleep(..)) when used in an awaited async task behave like multi threading?

在c#中,线程是从线程池中派生出来的(我认为每个处理器核心有一个线程,请纠正我)。但是,与 python 不同,我们无法控制在 c# 中显式运行代码以针对单个或特定数量的处理器,对吗?

【问题讨论】:

您的问题是在 C# 中是否有办法区分在单个 cpu 内核上运行的线程与在不同 cpu 内核上运行的线程?我认为答案是否定的,但我不确定我是否正确阅读了您的问题。但是每个 cpu 核心肯定可以有多个线程。 我可以将我的代码定位为仅在 2 个处理器上运行吗? 我不相信你能做到这一点。但是,即使在控制台应用程序中,您也可以启动“公寓”线程,因此在这方面它的行为类似于 WinForms 或 WPF 应用程序。但是,如果您启动第二个单元线程(您也可以这样做),我认为您无法控制它执行的 CPU 内核。 可能会是不同的,但不能保证。 await 下面的任何一种方式代码都将在 ui 线程上运行 - 不一定 Caius - 为什么不一定有任何例子?除非您在 Task.Run 中的代码中谈论 await? 【参考方案1】:

看看ProcessorAffinity。但是提醒一下:选择在单CPU核上运行并不能解决线程同步问题。

【讨论】:

【参考方案2】:

我们需要在这里解决几件事:

Python 多处理

Python 多处理更多地是指进程的概念,而不是处理器(如在 CPU 内核中)。这个想法是,您可以使用它自己的解释器运行时(因此它是自己的 GIL)、内存空间等在完全不同的进程中启动代码部分......

并不是说单个 Python 进程不能同时使用多个内核!

这是关于 Python 的一个非常常见的误解。许多 Python 操作不持有 GIL。您可以通过编写一个主要执行繁重的 NumPy 操作的函数来创建自己的示例,并同时在 20 个不同的线程中启动该函数。如果您检查 CPU 使用率,您会看到 20 个内核的使用率约为 100%。您还可以以不包含 GIL 的方式使用 Numba 到 JIT 函数。

限制核心使用

使用 2 个特定内核的方法是通过操作系统更改进程的处理器亲和性。我想更多地了解您为什么要限制核心使用,因为听起来您正在尝试做一些可以更干净地实现的事情。也许您想运行一些后台任务,但需要Semaphore 以确保一次最多只能运行 2 个任务?

线程与异步/等待

整个 async/await 语法的重点是让您免于微管理线程。这个想法是,您作为程序员编写您想要异步执行的操作,或者换句话说,哪些操作不应该阻止调用方法的执行,而 JIT/Runtime 将为您处理执行。可以是单线程,多线程等等……

在您提到的 WPF 中,您需要记住某些事情只能在 UI 线程上完成。但是,理想情况下,我们希望尽可能少地占用 UI 线程,因此我们使用任务来完成大部分工作。我们也可以在后台Tasks中使用Dispatcher来处理特定的UI线程代码。

等待之后

你提到过:

await 下方的任何一种方式代码都将在 ui 线程上运行。

默认情况下,“等待”之后的代码将尝试在前一个上下文中恢复。在 UI 线程中,这意味着两个部分(等待之前和之后)都将在 UI 线程上执行。但是,可以通过对正在等待的任务使用 ConfigureAwait(false) 调用来避免这种情况(并且应该避免多次)。

【讨论】:

除了这些很棒的信息之外,如果您确实想对任务和线程进行微管理,因为 C# 是自举的,您还可以创建自己的 TaskScheduler 实现和强制所有或部分任务在 n 个线程上运行。但这仍然不能解决操作系统跨多个内核管理内核负载的问题。

以上是关于在 c# 中,我们是不是可以控制在单个处理器或一定数量的处理器上显式运行代码?的主要内容,如果未能解决你的问题,请参考以下文章

在c# chart柱状图中,怎么根据数值的大小动态控制单个柱体的颜色

如何在 C# 中等待单个事件,带有超时和取消

访问控制

#yyds干货盘点# C#中类的异常处理

谈Swift中的访问控制

家居物联网(IoT)接入控制与认证的再思考