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



来自python背景,我们有:

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

  2. 多线程-通过单个处理器内的线程并行运行多个任务。理想的io绑定任务。Io绑定只是因为GIL的存在,这导致在任何时间点只有一个线程能够使用处理器。

  3. Async await-为io绑定任务做协同编程。

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

在c#的情况下,我正在学习:我们既可以显式地启动线程,也可以使用async await和Task的概念。运行,隐式处理线程创建和管理。

Async await适用于io绑定任务。Async await + Task。Run对于cpu受限的任务是很好的。

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

然而,如果我们使用async await + Task。运行(这对cpu受限的任务很好),然后在Task内运行代码。

无论哪种方式,await下面的代码都将在ui线程上运行。

在控制台应用程序中,我们是使用async await还是async await + Task。运行时,由于没有同步上下文,await之后的代码将由多个线程(来自线程池)并行运行。下面是一个例子:在控制台应用程序中,为什么同步阻塞代码(Thread.Sleep(..))在等待异步任务中使用时表现得像多线程?

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

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

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

Python多处理

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

这并不意味着一个Python进程不能同时使用多个内核!

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

限制核心使用

使用2个特定内核的方法是通过操作系统通过改变进程的处理器亲和度。我想知道你们为什么想要限制硬核的使用,因为听起来你们想要做一些更干净的事情。也许你想运行一些后台任务,但需要一个信号量,以确保一次最多只有2个任务运行?

线程vs Async/Await

整个async/await语法的意义在于使您不必对线程进行微管理。其思想是,作为程序员,您可以异步编写想要执行的操作,或者换句话说,哪些操作不应该阻塞调用方法的执行,JIT/Runtime将为您处理执行。它可以在一个线程,多个线程,等等…

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

在await

之后你提到过:

无论哪种方式,await下面的代码都将在ui线程上运行。

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

最新更新