c# 并发编程系列之二:并发产生的问题

  在上一篇中介绍了进程、线程、线程池的概念后,本篇我们再进一步,看看并发产生时操作系统是如何执行的,

以及并发给编程带来哪些和传统编程不一样的问题。

 

一、并发(Concurrency)

    定义:同时做多件事情。

    解释:比如GUI程序中用户输入数据时同时对数据做处理;WEB服务器同时处理多个用户请求等。这里同时指

    的是一个时间区间,而不是一个时间点。

 

二、多线程(Multithreading)

    从操作系统的角度来说,多线程是一个时间段内有多个线程在运行,这多个线程可以属于同一个进程也可以属于

不同的进程。

    从编程的角度来说,多线程特指某个进程内有1个以上的线程在CPU上执行指令, 在这多个线程中,有一个是主线程,

其他的线程是由主线程创建的子线程,多线程是并发的微观表现形式。

 

三、多线程的2种运行情况。

    第一种:单核CPU

    CPU的执行是分时间片来进行的。

 

    在单核CPU的情况下,操作系统会分配一定的CPU时间片给线程去执行指令,线程在使用完时间片后被挂起,系统再将

CPU时间片分配给下一个线程,每个时间片依次轮流地执行处理各个应用程序的线程指令,由于单个时间片很短,相对于

一个应用程序来说,就好像是处理器在为自己单独服务一样,从而达到多个应用程序在同时运行的效果。

 

    系统中的多个线程是如何被选中去使用CPU时间片的呢?是根据线程的优先级由操作系统的调度算法来对线程进行调度的。

一般线程的优先级由两部分决定:自身在进程中的相对优先级和所属进程的优先级。

 

    既然单核CPU在某个时间片内只能执行一个线程的指令,因此,要想我们的应用程序获得更快的执行速度,在所有线程

优先级相同的情况下,理论上应用程序就应该开启多个线程来增加被CPU执行的次数。从而提高运行速度,这是我们进行

多线程编程的出发点。

 

    注:实际情况不一定如此,因为即使优先级相同,线程的创建、启动、切换、相互间的通讯、同步、回收、销毁等都是

有时间开销的,所以在单核CPU的情况下,如果没有I/O等耗时操作,应用程序使用多线程并不一定运行更快,这个要特别注意。

 

    单核CPU的结构如下图:

    c# 并发编程系列之二:并发产生的问题

   

    第二种:多核CPU

    (1)多核的 "核" 究竟指的是什么

   CPU的主频越高运行越快,但是主频的增长不可能无极限的,在这种情况下,要获得更快的计算速度和更低的功耗,

芯片设计者开始绕过主频,转而在一块CPU中增加多个核心(Core),这里的核心就是指 运算单元+寄存器。

 

    (2)多核CPU的运行

    在多核CPU的情况下,一个CPU有多个处理核心(即多个逻辑处理器,如下图,笔者的电脑就有12个),操作系统可以

将多个线程分别发送到不同的逻辑处理器去运行,让线程的执行做到真正的并行(Parallel)。

c# 并发编程系列之二:并发产生的问题   c# 并发编程系列之二:并发产生的问题

    (3)多核CPU的调度

    多核CPU的任务调度算法有全局队列调度和局部队列调度。

    全局队列调度:操作系统维护一个全局的任务等待队列,当系统中有一个CPU核心空闲时,操作系统就从全局任务等待队列中

选取就绪任务开始在此核心上执行。这种算法的优点是CPU核心利用率较高。

    局部队列调度:操作系统为每个CPU内核维护一个局部的任务等待队列,当系统中有一个CPU内核空闲时,便从该核心的任务

等待队列中选取恰当的任务执行,这种方法的优点是任务基本上无需在多个CPU核心间切换,有利于提高CPU核心局部Cache命中率。

    目前多数多核CPU操作系统采用的是基于全局队列的任务调度算法。

 

四、并发带来的问题

    不管是单核CPU还是多核CPU,在进行并发编程时都会产生下面的问题

    1. 数据一致性

    2. 上下文切换

    并发编程就是在利用多线程好处的时候处理好它带来的种种问题。

 

c# 并发编程系列之二:并发产生的问题

上一篇:C# 输入日志文件方法


下一篇:FastAPI系列 路径操作配置