.NET教程 - 进程 & 异步 & 并行 第三部分(Process & Asynchronous & Parallelization Part3)

Timer

Timer介绍#

Timer介绍#

定时器提供一个时间间隔执行一段函数

当定时器启动时,系统自动创建一个新的线程,执行指定的函数

.NET中的 3 个 Timer 类#

System.Windows.Forms.Timer

System.Threading.Timer

System.Timers.Timer

三种Timer对比图#

image

System.Windows.Forms.Timer介绍#

这个 Timer 在激发 Timer.Tick 事件的时候

事件的处理函数是在程序主线程上执行的

所以在 WinForm 上面用这个 Timer 很方便

因为在 From 上的所有控件都是在程序主线程上创建的

那么在 Tick 的处理函数中可以对 Form 上的所有控件进行操作

不会造成 WinForm控件的线程安全问题

System.Threading.Timer 和 System.Timers.Timer共同点#

这2个 Timer 运行的核心都是 System.Threading.ThreadPool 线程池

Timer 每到间隔时间后就会激发响应事件

因此要申请线程来执行对应的响应函数

Timer 将获取线程的工作都交给了线程池来管理

每到一定的时间后它就去告诉线程池

我现在激发了个事件要运行对应的响应函数,麻烦你给我向操作系统要个线程

申请交给你了,线程分配下来了你就运行我给你的响应函数

没分配下来先让响应函数在这儿排队(操作系统线程等待队列)

消息已经传递给线程池了 Timer 也就不管了

因为它还有其他的事要做(每隔一段时间它又要激发事件)

至于提交的请求什么时候能够得到满足,要看线程池当前的状态

线程池当前的状态:

1)如果线程池现在有线程可用,那么申请马上就可以得到满足,

有线程可用又可以分为两种情况:

<1>线程池现在有空闲线程,现在马上就可以用

<2>线程池本来现在没有线程了,但是刚好申请到达的时候

​ 有线程运行完毕释放了,那么申请就可以用别人释放的线程

这两种情况情况就如同你去游乐园玩赛车

如果游乐园有 10 辆车,现在有 3 个人在玩,那么还剩 7 辆车,你去了可以直接开

另外还有一种情况就是你到达游乐园前 10 辆车都在开

但是你运气很好,刚到游乐园就有人不玩了,正好你坐上去就可以接着开

2)如果现在线程池现在没有线程可用,也分为两种情况:

<1>线程池现有线程数没有达到设置的最大工作线程数

​ 那么隔半秒钟.net framework 就会向操作系统申请一个新的线程

<2>线程池现有工作线程数达到了设置的最大工作线程数,那么申请只有在等待队

​ 列一直等下去,直到有线程执行完任务后被释放

System.Threading.Timer#

System.Threading.Timer定时器说明#

定时器提供一个时间间隔执行一段函数

当定时器启动时,系统自动创建一个新的线程,执行指定的函数

作用是每到间隔时间后激发响应事件并执行相应函数

执行响应函数要向线程池申请线程

注意的:System.Threading.Timer 在创建对象后立即开始执行

比如:

System.Threading.Timer timer = new System.Threading.Timer(Excute, null, 0, 10);

这句执行完后每隔 10 毫秒就执行Excute 函数

实例#

实例:定时器运行任务#

using System;
using System.Threading;

namespace PandaNamespace
{
    public class PandaClass
    {
        public static void Main()
        {
            //传入定时器的参数
            object data = "Panda666.com";
            //新建定时器
            Timer timer = new Timer(PandaClass.SomeMethod,data,0,2000);
            //修改定时器开始时间和间隔
            timer.Change(1000, 3000);

            Console.ReadKey();
            //销毁定时器
            timer.Dispose();
        }

        /// <summary>
        /// 定时任务
        /// </summary>
        /// <param name="arg"></param>
        public static void SomeMethod(object arg)
        {
            Console.WriteLine(arg.ToString());
        }
    }
}

实例:多次执行Timer并指定执行次数#

说明:用了个 bool 类型的变量 flag 来判断当前是否执行到 80 次了

执行到 80次后将 flag 置为 false,然后 timer.Dispose

这时虽然任务还是要多执行很多次但是由于 flag为 false

Excute 函数一开始就做了判断 flag 为 false 会立即退出

代码:

using System;
using System.Threading;
namespace ConsoleApp4
{
    /// <summary>
    /// 测试
    /// </summary>
    class SafeTimerTest
    {
        /// <summary>
        /// Timer真实执行了多少次
        /// </summary>
        public static int _TimerRealDoCount = 0;
        
        /// <summary>
        /// 被测试的计时器
        /// </summary>
        public static System.Threading.Timer timer;
        
        /// <summary>
        /// 计时器是否继续运行
        /// </summary>
        public static bool _IsTimerContineDo = true;
        
        /// <summary>
        /// 用于锁使用的上下文
        /// </summary>
        private static object mylock = new object();
       
        /// <summary>
        /// 测试使用的模拟真实计时器任务
        /// </summary>
        /// <param name="obj"></param>
        static void Excute(object obj)
        {
            //将当前线程设置为前台线程
            Thread.CurrentThread.IsBackground = false;

            //锁定上下文
            lock (mylock)
            {
                if (!_IsTimerContineDo)
                {
                    return;
                }

                //执行次数增加
                ++_TimerRealDoCount;

                //检测是否到达80次
                if (_TimerRealDoCount == 80)
                {
                    //将计时器的停止操作标志位设置为不继续执行
                    _IsTimerContineDo = false;
                    //释放计时器
                    timer.Dispose();
                }

                //输出当前的线程数量
                Console.WriteLine("Now:" + _TimerRealDoCount.ToString());
            }

            //模拟花时间的代码
            Thread.Sleep(10);
        }

        /// <summary>
        /// 调用测试
        /// </summary>
        public static void Init()
        {
            //调用
            timer = new System.Threading.Timer(Excute, null, 0, 10);
        }
    }

    class Program
    {
        static void Main()
        {
            //开启测试
            SafeTimerTest.Init();

            //wait
            Console.ReadKey();
        }
    }
}

System.Timers.Timer#

说明#

System.Timers.Timer 和 System.Threading.Timer 的区别#

1、System.Timers.Timer 构造函数可以什么事情也不做,也可以传入响应间隔时间

System.Timers.Timer timer = new System.Timers.Timer(10); 

2、System.Timers.Timer 响应事件的响应函数不在构造函数中设置

timer.Elapsed += new ElapsedEventHandler(timer_Elapsed); 

3、声 明 System.Timers.Timer 对象后不会自动执行

需要调用 timer.Start() 或 者 timer.Enabled = true 来启动它

timer.Start()的内部原理还是设置 timer.Enabled = true

调用 timer.Stop()或者 timer.Enabled = false 来停止引发 Elapsed 事件

timer.Stop()的内部原理还是设置 timer.Enabled = false

最重要的是 timer.Enabled = false 后会取消线程池中当前等待队列中剩余任务的执行

实例#

新建定时器#

//新建一个Timer
Timer timer1 = new Timer();
//指定间隔时间
Timer timer2 = new Timer(2000);
设置/获得定时器的运行时间间隔
Timer timer1 = new Timer();
timer1.Interval = 2000;
Console.WriteLine(timer1.Interval);

定义定时器事件#

Timer timer1 = new Timer();
timer1.Elapsed += PandaClass.timer_Elapsed;
public static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
    Console.WriteLine("Panda666.com");
}

关闭和启用定时器#

Timer timer1 = new Timer();
timer1.Enabled = true;
timer1.Enabled = false;
Console.WriteLine(timer1.Enabled);

启动和停止定时器#

Timer timer1 = new Timer();
timer1.Start();
timer1.Stop();

销毁定时器#

Timer timer1 = new Timer();
timer1.Dispose();

实例:计时器执行80次然后停止#

using System;
using System.Threading;
using System.Timers;

namespace ConsoleApp4
{
    /// <summary>
    /// 测试
    /// </summary>
    class SafeTimerTest
    {
        /// <summary>
        /// Timer真实执行了多少次
        /// </summary>
        public static int _TimerRealDoCount = 0;

        /// <summary>
        /// 被测试的计时器
        /// </summary>
        public static System.Timers.Timer _Timer;

        /// <summary>
        /// 计时器是否继续运行
        /// </summary>
        public static bool _IsTimerContineDo = true;

        /// <summary>
        /// 用于锁使用的上下文
        /// </summary>
        private static object _LockContext = new object();

        /// <summary>
        /// 测试使用的模拟真实计时器任务
        /// </summary>
        /// <param name="obj"></param>
        static void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            //切换线程为前台线程
            Thread.CurrentThread.IsBackground = false;
            //锁定上下文
            lock (_LockContext)
            {
                //确定标志位是否可以继续执行
                if (!_IsTimerContineDo)
                {
                    return;
                }
                //执行次数增加
                ++_TimerRealDoCount;
                //输出当前执行次数
                Console.WriteLine("Now:" + _TimerRealDoCount.ToString());
                //到达80次停止执行
                if (_TimerRealDoCount == 80)
                {
                    _Timer.Stop();
                    _IsTimerContineDo = false;
                }
            }

            //模拟执行很多次的任务
            Thread.Sleep(1000);
        }

        /// <summary>
        /// 调用测试
        /// </summary>
        public static void Init()
        {
            _Timer = new System.Timers.Timer(10);
            _Timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
            _Timer.Start();
        }
    }

    class Program
    {
        static void Main()
        {
            //开启测试
            SafeTimerTest.Init();

            //wait
            Console.ReadKey();
        }
    }
}

Process-Process进程基础

进程介绍#

进程是对一段静态指令序列(程序)的动态执行过程。与进程相关的信息包括进程的用户标志、正在执行的已经编译好的程序、进程程序、数据在存储器中的位置等。在.NET Framework中,Process类提供了对进程进行管理的各种方法

进程 = 线程 + 共享资源

进程是一个具有一定独立功能的程序在一个数据集合上依次动态执行的过程

进程是一个正在执行程序的实例,包括程序计数器、寄存器和程序变量的当前值

进程就是一个程序的执行流程,内部保存程序运行所需的资源

在操作系统中可以有多个进程在运行

可对于CPU来说,同一时刻,一个CPU只能运行一个进程

但在某一时间段内,CPU将这一时间段拆分成更短的时间片

CPU不停的在各个进程间游走,这就给人一种同时运行的错觉

像CPU可以同时运行多个进程一样,这就是伪并行

进程生命周期#

进程创建

创建进程有三个主要事件

\1. 系统初始化

\2. 用户请求创建一个新进程

\3. 一个正在运行的进程执行创建进程的系统调用

进程运行

内核选择一个就绪的进程,让它占用处理机并运行

这里就涉及到了进程的调度策略选择哪个进程调度

为什么选择调度这个进程

进程等待

在以下情况下进程会等待(阻塞)

请求并等待系统服务,无法马上完成

启动某种操作,无法马上完成

需要的数据没有到达

注意:进程只能自己阻塞自己,因为只有进程自身才能知道何时需要等待某种事件的发生

进程唤醒

进程只能被别的进程或操作系统唤醒,唤醒进程的原因有:

被阻塞进程需要的资源可被满足

被阻塞进程等待的事件到达

将该进程的PCB插入到就绪队列

进程结束

在以下四种情况下进程会结束:

自愿型正常退出

自愿型错误退出

强制型致命错误退出

强制型被其它进程杀死退出

进程状态#

不同系统设置的进程状态是不同的

多数系统中的进程在生命结束前有三种基本状态

image

进程只会处于三种基本状态之一:

运行状态:进程正在处理机上运行时就处在运行状态,该时刻进程时钟占用着CPU;

就绪状态:万事俱备,只欠东风,进程已经获得了除处理机之外的一切所需资源,一旦得到处理机就可以运行;就绪态中的进程其实可以运行,但因为其它进程正在占用着CPU而暂时停止运行;

等待状态(阻塞状态):进程正在等待某一事件而暂停运行,等待某个资源或者等待输入输出完成。除非某种外部事件发生,否则阻塞态的进程不能运行;

在操作系统发现进程不能继续运行下去时,进程因为等待输入而被阻塞,进程从运行态转换到阻塞态

调度程序选择了另一个进程执行时,当前程序就会从运行态转换到就绪态!

被调度程序选择的程序会从就绪态转换到运行态!

当阻塞态的进程等待的一个外部事件发生时,就会从阻塞态转换到就绪态,此时如果没有其他进程运行时,则立刻从就绪态转换到运行态

什么是进程挂起?为什么会出现进程挂起?#

进程挂起就是为了合理且充分的利用系统资源,把一个进程从内存转到外存。进程在挂起状态时,意味着进程没有占用内存空间,处在挂起状态的进程映射在磁盘上。进程挂起通常有两种状态:

阻塞挂起状态:进程在外存并等待某事件的出现;

就绪挂起状态:进程在外存,但只要进入内存即可运行。

有什么与进程挂起相关的状态转换?

进程挂起可能有以下几种情况:

阻塞到阻塞挂起:没有进程处于就绪状态或就绪进程要求更多内存资源时,会进行这种转换,以提交新进程或运行就绪进程

就绪到就绪挂起:当有高优先级阻塞进程或低优先级就绪进程时,系统会选择挂起低优先级就绪进程

运行到就绪挂起:对于抢占式分时系统,当有高优先级阻塞挂起进程因事件出现而进入就绪挂起时,系统可能会把运行进程转到就绪挂起状态

阻塞挂起到就绪挂起:当有阻塞挂起进程有相关事件出现时,系统会把阻塞挂起进程转换为就绪挂起进程

有进程挂起那就有进程解挂:指一个进程从外存转到内存,相关状态有:

就绪挂起到就绪:没有就绪进程或就绪挂起进程优先级高于就绪进程时,就会进行这种转换

阻塞挂起到阻塞:当一个进程释放足够内存时,系统会把一个高优先级阻塞挂起进程转换为阻塞进程

进程终止#

进程发生终止可能是以下几种情况:

正常退出(自愿)

进程完成了工作正常终止,UNIX中退出进程的系统调用是exit

出错退出(自愿)

进程发现了错误而退出

严重错误(非自愿)

进程发生了严重的错误而不得不退出,通常是程序的错误导致

例如执行了一条非法指令,引用不存在的内存

或者除数是0等,出现这些错误时进程默认会退出

而有些时候如果用户想自行处理某种类型的错误

发生不同类型错误时进程会收到不同类型的信号

用户注册处理不同信号的函数即可

被其它进程杀死(非自愿)

其它进程执行kill系统调用通知操作系统杀死某个进程

什么是进程调度#

操作系统对于进程调度都有什么策略

当系统中有多个进程同时竞争CPU

如果只有一个CPU可用,那同一时刻只会有一个进程处于运行状态

操作系统必须要选择下一个要运行的是哪个进程

在操作系统中,完成选择工作的这部分称为调度程序

该程序使用的算法称作调度算法

什么时候进行调度#

系统调用创建一个新进程后,需要决定是运行父进程还是运行子进程

一个进程退出时需要做出调度决策,需要决定下一个运行的是哪个进程

当一个进程阻塞在I/O和信号量或者由于其它原因阻塞时,必须选择另一个进程运行

当一个I/O中断发生时,如果中断来自IO设备,而该设备现在完成了工作

某些被阻塞的等待该IO的进程就成为可运行的就绪进程了,是否让新就绪的进程运行

或者让中断发生时运行的进程继续运行

或者让某个其它进程运行,这就取决于调度程序的抉择了

调度算法的类型#

非抢占式调度算法

挑选一个进程,然后让该进程运行直至被阻塞,或者直到该进程自动释放CPU

即使该进程运行了若干个小时,它也不会被强迫挂起

这样做的结果是,在时钟中断发生时不会进行调度,在处理完时钟中断后

如果没有更高优先级的进程等待,则被中断的进程会继续执行

简单来说,调度程序必须等待事件结束。

非抢占方式引起进程调度的条件:

进程执行结束,或发生某个事件而不能继续执行

正在运行的进程因有I/O请求而暂停执行

进程通信或同步过程中执行了某些原语操作(wait、block等)

抢占式调度算法

挑选一个进程,并且让该进程运行某个固定时段的最大值

如果在该时段结束时,该进程仍在运行,它就被挂起

而调度程序挑选另一个进程运行,进行抢占式调度处理

需要在时间间隔的末端发生时钟中断,以便CPU控制返回给调度程序

如果没有可用的时钟,那么非抢占式调度就是唯一的选择

简单来说,就是当前运行的进程在事件没结束时就可以被换出

防止单一进程长时间独占CPU资源

抢占式调度算法:优先级算法、短作业优先算法、轮转算法等

调度策略#

不同系统环境下有不同的调度策略算法

调度算法也是有KPI的,对调度算法首先提的需求就是:

公平

调度算法需要给每个进程公平的CPU份额,相似的进程应该得到相似的服务

对一个进程给予较其它等价的进程更多的CPU时间是不公平的

执行力

每一个策略必须强制执行,需要保证规定的策略一定要被执行

平衡

需要保证系统的所有部分尽可能都忙碌

但是因为不同的应用有不同的目标

不同的系统中,调度程序的优化也是不同的

如何配置调度策略

调度算法有很多种,各有优缺点,操作系统自己很少能做出最优的选择,那么可以把选择权交给用户

由用户根据实际情况来选择适合的调度算法,这就叫策略与机制分离

调度机制位于内核,调度策略由用户进程决定,将调度算法以某种形式参数化

由用户进程来选择参数从而决定内核使用哪种调度算法

三种环境#

批处理系统#

批处理系统的管理者为了掌握系统的工作状态,主要关注三个指标:

吞吐量:是系统每小时完成的作业数量

周转时间:指从一个作业提交到完成的平均时间

CPU利用率:尽可能让CPU忙碌,但又不能过量

调度算法:

先来先服务

先来后到嘛,就像平时去商店买东西需要排队一样

使用该算法,进程按照它们请求CPU的顺序来使用CPU

该算法最大的优点就是简单易于实现,太容易的不一定是好的

该算法也有很大的缺点:平均等待时间波动较大,时间短的任务可能排队排在了时间长的任务后面

举个生活中的例子,排着队去取快递,如果每个人都很快取出来快递还好

如果前面有几个人磨磨唧唧到快递柜前才拿出手机打开app,再找半分钟它的取件码

就会严重拖慢后面的人取快递的速度,同理排着队的进程如果每个进程都很快就运行完还好

如果其中有一个得到了CPU的进程运行时候磨磨唧唧很长时间都运行不完,那后面的进程基本上就没有机会运行了

最短作业优先

该调度算法是非抢占式的算法,每个进程执行期间不会被打断

每次都选择执行时间最短的进程来调度,但问题来了

操作系统怎么可能知道进程具体的执行时间呢,所以该算法注定是基于预测性质的理想化算法

而且有违公平性,而且可能导致运行时间长的任务得不到调度

最短剩余时间优先

该调度算法是抢占式的算法,是最短作业优先的抢占版本

在进程运行期间,如果来了个更短时间的进程,那就转而去把CPU时间调度给这个更短时间的进程

它的缺点和最短作业优先算法类似

交互式系统#

对于交互系统最重要的指标就是响应时间和均衡性

响应时间

一个请求被提交到产生第一次响应所花费的时间

你给别人发微信别人看后不回复你或者几个小时后才回复你,你是什么感受,这还是交互式吗

均衡性

减少平均响应时间的波动

需要符合固有期望和预期,你给别人发微信,他有时候秒回复,有时候几个小时后才回复

在交互式系统中,可预测性比高差异低平均更重要

调度算法:

轮转调度

每个进程被分配一个时间段,称为时间片,即CPU做到雨露均沾

轮流翻各个进程的牌子,这段时间宠幸进程A,下一段时间宠幸进程B

再下一段时间宠幸进程C,确保每个进程都可以获得CPU时间,如果CPU时间特别短的话

在外部看来像是同时宠幸了所有进程一样

那么问题来了,这个时间片究竟多长时间好呢

如果时间片设的太短会导致过多的进程切换,频繁的上下文切换会降低CPU效率

而如果时间片设的太长又可能对短的交互请求的响应时间变长

通常将时间片设为20-50ms是个比较合理的折中

过去的经验是维持上下文切换的开销处于1%以内

优先级调度

上面的轮转调度算法是默认每个进程都同等重要,优先级相同

然而有时候进程需要设置优先级,例如某播放视频的前台进程可优先于某收发邮件的后台守护进程被调度

在优先级调度算法中,每个优先级都有相应的队列

队列里面装着对应优先级的进程,首先在高优先级队列中进行轮转调度

当高优先级队列为空时,转而去低优先级队列中进行轮转调度

如果高优先级队列始终不为空,那么低优先级的进程很可能就会饥饿到很久不能被调度

多级队列

多级队列算法与优先级调度算法不同

优先级算法中每个进程分配的是相同的时间片

而在多级队列算法中,不同队列中的进程分配给不同的时间片

当一个进程用完分配的时间片后就移动到下一个队列中

这样可以更好的避免上下文频繁切换

举例:有一个进程需要100个时间片,如果每次调度都给分配一个时间片

则需要100次上下文切换,这样CPU运行效率较低

通过多级队列算法,可以考虑最开始给这个进程分配1个时间片,然后被换出

下次分给它2个时间片,再换出,之后分给它4、8、16、64个时间片

这样分配的话,该进程只需要7次交换就可以运行完成

相比100次上下文切换运行效率高了不少

但顾此就会失彼,那些需要交互的进程得到响应的速度就会下降

最短进程优先

交互式系统中应用最短进程优先算法其实是非常适合的

每次都选择执行时间最短的进程进行调度,这样可以使任务的响应时间最短

但这里有个任务,还没有运行呢,我怎么知道进程的运行时间呢

根本没办法非常准确的再当前可运行进程中找出最短的那个进程

有一种办法就是根据进程过去的行为进行预测,但这能证明是个好办法吗

保证调度

这种调度算法就是向用户做出明确的可行的性能保证,然后去实现它

一种很实际的可实现的保证就是确保N个用户中每个用户都获得CPU处理能力的1/N

类似的,保证N个进程中每个进程都获得1/N的CPU时间

彩票调度

彩票调度算法基本思想是为进程提供各种资源(CPU时间)的彩票

一旦需要做出调度决策时,就随机抽出一张彩票,拥有该彩票的进程获得该资源

很明显,拥有彩票越多的进程,获得资源的可能性越大

该算法可以理解为股票算法,将CPU的使用权分成若干股

假设共100股分给了3个进程,给这些进程分别分配20、30、50股

那么它们大体上会按照股权比例(20:30:50)划分CPU的使用

公平分享调度

假设有系统两个用户,用户1启动了1个进程,用户2启动了9个进程

如果使用轮转调度算法,那么用户1将获得10%的CPU时间,用户2将获得90%的CPU时间

这对用户来说公平吗?如果给每个用户分配50%的CPU时间,那么用户2中的进程获得的CPU时间明显比用户1中的进程短

这对进程来说公平吗?这就取决于怎么定义公平啦?

Process-实时系统#

实时系统顾名思义,最关键的指标当然是实时

满足截止时间:需要在规定deadline前完成作业

可预测性:可预测性是指在系统运行的任何时刻,在任何情况下,实时系统的资源调配策略都能为争夺资源的任务合理的分配资源,使每个实时任务都能得到满足。

调度算法分类:

硬实时

必须在deadline之前完成工作,如果delay,可能会发生灾难性或发生严重的后果

软实时

必须在deadline之前完成工作,但如果偶尔delay了,也可以容忍

调度算法:

单调速率调度

采用抢占式、静态优先级的策略,调度周期性任务

每个任务最开始都被配置好了优先级

当较低优先级的进程正在运行并且有较高优先级的进程可以运行时

较高优先级的进程将会抢占低优先级的进程

在进入系统时,每个周期性任务都会分配一个优先级,周期越短,优先级越高

这种策略的理由是:更频繁的需要CPU的任务应该被分配更高的优先级

最早截止时间调度

根据截止时间动态分配优先级,截止时间越早的进程优先级越高

该算法中,当一个进程可以运行时,它应该向操作系统通知截止时间

根据截止时间的早晚,系统会为该进程调整优先级,以便满足可运行进程的截止时间要求

它与单调速率调度算法的区别就是一个是静态优先级,一个是动态优先级

操作系统怎么完成进程调度#

进程的每次变化都会有相应的状态,而操作系统维护了一组状态队列

表示系统中所有进程的当前状态;不同的状态有不同的队列,有就绪队列阻塞队列等

每个进程的PCB都根据它的状态加入到相应的队列中

当一个进程的状态发生变化时,它的PCB会从一个状态队列中脱离出来加入到另一个状态队列

image

注意

图中同一种状态为什么有多个队列呢

因为进程有优先级概念,相同状态的不同队列的优先级不同

进程间通信有几种方式#

由于各个进程不共享相同的地址空间,任何一个进程的全局变量在另一个进程中都不可见

所以如果想要在进程之间传递数据就需要通过内核

在内核中开辟出一块区域,该区域对多个进程都可见,即可用于进程间通信

有读者可能有疑问了,文件方式也是进程间通信啊,也要在内核开辟区域吗

这里说的内核区域其实是一段缓冲区,文件方式传输数据也有内核缓冲区的参与(零拷贝除外)

image

Process-C#与进程

C#进程开发说明#

使用System.Diagnistics.Process类实现进程管理

通过实例化Process类实现启动一个独立的进程

使用Process组件,实现对本地和远程进程管理

命名空间#

System.Diagnostics;

所在命名空间:

using System.Diagnostics;

Process-Process类

说明#

注意:Windows Store apps不可以使用该类型

命名空间

using System.Diagnostics;

实例:Windows Store apps可以使用Windows.System.Launcher类型来启动其他应用

Launcher.LaunchUriAsync (new Uri ("http://albahari.com"));
var file = await KnownFolders.DocumentsLibrary.GetFileAsync ("foo.txt");
Launcher.LaunchFileAsync (file);

常用API#

获取此进程的总的处理器时间#

TotalProcessorfime 属性

此值是UserProcessorTime和PrivilegedProcessorTime 的和

UserProcessorTime获取此进程的用户处理器时间

PrivilegedProcessorTimc是获取此进程的特权处理器时间

获取为进程分配的物理内存量(字节数)#

WorkingSet64 属性 获取为进程分配的物理内存量(字节数)

获取进程使用的最大物理内存量(字节数)#

PeakWorkingSet64 属性 获取进程使用的最大物理内存量(字节数)

获取用于写入应用程序输人的流#

StandardInput 属性

Process可以读取标准输入流(一般是键盘)的输入文本

也可以通过重定向StandardInput流以编程方式指定输入信息

例如:

不使用键盘输人,而从指定文件的内容或另一个应用程序的输出提供文本

若要正常使用 StandardInput

必须将ProccssStartlnfo.UseShellExecute设置为 false

并且将 ProcessStartInfo.RedirectStandardInput 设置为 true

获取用于读取应用程序输出的流#

StandardOutput 属性

当Process将文本写人其标准流中时,一般在控制台上显示该文本

通过重定向Standardoutput流,可以操作或取消进程的输出

例如:可以筛选文本、用不同方式将其格式化

也可以将输出同时写入控制台和指定的日志文件中

释放与此组件关联的所有资源#

Close()

通过向进程的主窗口发送关闭消息来关闭具有用户界面的进程#

CloseMainWindow()

如果成功发送了关闭消息,则返回true

如果关联进程没有主窗口或禁用了主窗口(例如:当前正在显示模式对话框),则返回false

通过关闭主窗口发出的退出进程的请求不会强制应用程序立即退出

若要强制应用程序退出,应该使用Kill方法

注意只能对在本地计算机上运行的进程调用Kill和CloseMainWindow 方法

通过进程Id获得Process#

GetProcessById()

并将其与本地计算机上的进程资源关联

在任何特定计算机上,进程的标识符都是唯一的

GetProcessByld最多返回一个进程

要获取正在运行某特定应用程序的所有进程,应该使用GetProcessesByName方法

如果运行指定应用程序的计算机上存在多个进程

则GetProcessesByName方法返回一个包含所有关联进程的数组

可以依次查询这些进程中的每一个标识符

获得所有Process#

GetProcesses()

并将它们与本地计算机上的所有进程资源关联

通过进程名称获得Process#

GetProcessesByName()

并将它们与本地计算机上运行同一可执行文件的所有进程资源关联
注意进程名是不包括.exe扩展名或路径的进程名称

立即强制终止关联的进程#

Kill()

注意由于Kill方法是异步执行的,因此在调用Kill方法后
还要调用WaitForExit方法等待进程退出,或者检查HasExited属性以确定进程是否已经退出
对于有界面的应用程序,一般使用CloseMainWindow方法
而不是Kill方法,如果CloseMainWindow失败,则可以使用Kill方法终止进程
Kill方法是终止没有图形化界面的进程的唯一方法

重新获取关联进程信息#

Refresh()

启动进程资源并将其与Process组件关联#

Start()

设置等待关联进程退出的时间#

WaitForExit()

并在该段时间结束前或该进程退出前,阻止当前线程执行

使Process组件等待关联进程进入空闲状态#

WaitForlnputldle 方法
仅适用于具有用户界面的进程
如果关联进程已经达到空闲状态,则返回true;否则返回falseo
此状态很有用,例如,应用程序需要等待启动的进程完成创建其主窗口
然后该应用程序才能与该窗口通信

实例#

新建进程#

//创建一个进程
this.process = new Process();

关闭进程#

//关闭进程
this.process.Close();

关闭进程(发送关闭消息|关闭窗口)#

this.process.CloseMainWindow();
开启进程
string filePath = @"C:\Windows\System32\mspaint.exe";
this.process.StartInfo.FileName = filePath;
//开启进程
this.process.Start();

实例:启动其他程序#

Process.Start ("notepad.exe");
Process.Start ("notepad.exe", "e:\\file.txt");

实例:启动其他应用#

ProcessStartInfo psi = new ProcessStartInfo
{
    FileName = "cmd.exe",
    Arguments = "/c ipconfig /all",
    RedirectStandardOutput = true,
    UseShellExecute = false
};
Process p = Process.Start (psi);
string result = p.StandardOutput.ReadToEnd();
Console.WriteLine (result);

搜索指定进程(通过进程名)#

//获得指定名称的进程
Process[] processList = Process.GetProcessesByName("mspaint");
for (int i = 0; i < processList.Length; i++)
{
    processList[i].CloseMainWindow();
    processList[i].Close();
}

搜索指定进程(通过进程Id)#


获取远程计算机的所有进程#

Process[] myProcesses = Process.GetProcesses(remoteMachineName);
Process[] myProcesses = Process.GetProcesses("192.168.0.1");

获取远程计算机上指定名称的进程#

Process[] myProcesses = Process.GetProcessesByName(processName,remoteMachineName);

获得进程优先级(只读)#

BasePriority只读属性
注意:返回值是int类型
返回值如下:
4: 表示空闲优先级(Idle)
即此进程中的线程只能在系统空闲时运行,如屏幕保护程序等
更高优先级类中运行的任何进程中的线程都优先于此进程中的线程
8 :表示Normal优先级
13:表示High优先级
即进程必须立即执行的时间关键任务
该进程中的线程优先于普通或空闲优先级进程中的线程
24:表示RealTime优先级
它指定进程拥有可能的最高优先级
由于此优先级最高
当该进程耗时较长时,可能导致磁盘缓存不刷新,或导致鼠标无响应
所以一般不使用此优先级
实例:

Console.WriteLine(currentProcces.BasePriority);

设置或更改进程优先级#

PriorityClass属性
是一个PriorityClass类型
可取的枚举值有6个 :Idle、BelowNormal、Normal、AboveNormalx、High、RealTime
实例:

currentProcces.PriorityClass = ProcessPriorityClass.Normal

获取关联进程终止时指定的值#

ExitCode属性
一般用为零的ExitCode值指示成功退出
并通过非零值指出错误
该值只能在HasExited属性为True时才能检测

实例:

if(currentProcces.HasExited)
{
    Console.WriteLine(currentProcces.ExitCode);
}

获取关联进程退出的时间#

ExitTime属性
该值只能在HasExited属性为True时才能检测
实例:

if(currentProcces.HasExited)
{
    Console.WriteLine(currentProcces.ExitTime);
}

获得关联进程是否已终止#

HasExited属性
实例:

if(currentProcces.HasExited)
{
    Console.WriteLine(currentProcces.ExitTime);
}

获取关联进程的唯一标识符#

Id属性
实例:

Console.WriteLine(currentProcces.Id);

获取该进程的名称#

ProcessName 属性
该名称不包括文件扩展名和路在路径
实例:

Console.WriteLine(currentProcces.ProcessName);

获取关联进程正在其上运行的计算机的名称#

MachineName属性
该属性一般用于查看远程计算机上运行的进程信息
如果查看本机的计算机名称,应该使用Environmcnt.MachincName
实例:

Console.WriteLine(currentProcces.MachineName);

获取或设置要传递给启动进程的文件名以及启动参数#

StartInfo属性
类似于命令行方式输入的命令和参数
实例:

Console.WriteLine(currentProcces.StartInfo.FileName);
Console.WriteLine(currentProcces.StartInfo.UseShellExecute);
Console.WriteLine(currentProcces.StartInfo.CreateNoWindow);
Console.WriteLine(currentProcces.StartInfo.ArgumentList.Count);
Console.WriteLine(currentProcces.StartInfo.Domain);

获取关联进程启动的时间#

StartTime属性
实例:

Console.WriteLine(currentProcces.StartTime);

获取在关联进程中运行的一组线程#

Threads 属性
实例:

for (int i = 0; i < currentProcces.Threads.Count; i++)
{
    Console.WriteLine(currentProcces.Threads[i].Id);
}

获取关联进程的主模块#

MainModule 属性
即加载到特定进程中的dll或.exe文件
一般用该属性查看启动进程的可执行文件的信息
包括模块名、文件名和模块占用的内存等详细信息
实例:

Console.WriteLine(currentProcces.MainModule.FileName);
Console.WriteLine(currentProcces.MainModule.ModuleName);
Console.WriteLine(currentProcces.MainModule.BaseAddress);
Console.WriteLine(currentProcces.MainModule.EntryPointAddress);
Console.WriteLine(currentProcces.MainModule.FileVersionInfo.FileVersion);

获取由关联进程加载的模块#

Modules属性
即加载到特定进程中的.dll或.exe文件
如果该进程有主窗口,则可以先调用WaitForlnputldle方法
然后再检索此属性以确保获取列表时该集合非空
实例:

Console.WriteLine(currentProcces.Modules.Count);

调用CMS执行命令#

//创建进程
Process process = new Process();
//设置进程启动的程序为控制台程序
process.StartInfo.FileName = "cmd.exe";
//设置进程使用Shell执行
process.StartInfo.UseShellExecute = false;
//重定向标准输入
process.StartInfo.RedirectStandardInput = true;
//不创建窗口
process.StartInfo.CreateNoWindow = true;
//执行进程
process.Start();
//命令
string command = "mspaint.exe";
//给进程输入命令
process.StandardInput.WriteLine(command);
//刷新命令并执行
process.StandardInput.AutoFlush = true;
//等待进程完成
process.WaitForExit();
//关闭进程
process.Close();

死锁(Deadlock)

What is a Deadlock in C#?#

In simple words, we can define a deadlock in C# is a situation where two or more threads are unmoving or frozen in their execution because they are waiting for each other to finish.

image

作者:重庆熊猫

出处:https://www.cnblogs.com/cqpanda/p/16749433.html

版权:本作品采用「不论是否商业使用都不允许转载,否则按3元1字进行收取费用」许可协议进行许可。

posted @   重庆熊猫  阅读(137)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示