dotnet 6 创建进程 Process.Start 时设置 UseShellExecute 在 Windows 下对性能的影响
本文将告诉大家,在 dotnet 6 或 dotnet 7 版本里,启动新的进程时,在 StartInfo 设置 UseShellExecute 为 true 和 false 时,对性能的影响
在 dotnet 6 或 dotnet 7 版本里,其他的版本我没有测试和去了解哈,启动新的进程时,在 StartInfo 设置 UseShellExecute 为 true 时,且当调用线程非 STA 时,在 Windows 下,性能会较差
为什么性能会比较差?下面将从 dotnet 源代码的角度来告诉大家
开始之前,回顾一下 UseShellExecute 属性的作用,在 Process.Start 里,是允许调用 Shell 打开进程的,传入的不一定要求是一个 exe 等可执行文件,还可以是某个文件,例如 txt 文件。传入文件时,系统将会根据默认打开程序,使用文件的默认打开程序打开文件,例如 txt 文件默认将使用记事本程序打开。想要实现此效果,就需要将 UseShellExecute 设置为 true 的值
设置为 true 的值,在 dotnet 底层将会调用 win32 的 ShellExecuteExW 函数
而对于打开某个 exe 来说,很多时候,除非是需要加上 verb 等,否则也是不需要用到 ShellExecuteExW 启动的。换句话说,如果明确知道是启动一个进程,只是启动时传传参数等,且没有其他的需求,可以放心设置 UseShellExecute 为 false 的值,当然,为 false 也是默认值
为什么将 UseShellExecute 设置为 true 的性能比较差?这还需要从 dotnet 的调用 ShellExecuteExW 函数方法开始聊起
在 dotnet 的 Process.Start 方法里面,有许多重载方法,最终都会调进去 public bool Start() 方法里面,在此方法里,将进入平台有关的 StartCore 方法
这里只讨论 Windows 下的 StartCore 方法的实现,其实现是根据 Windows 下的创建进程使用的 CreateProcessW 和 ShellExecuteExW 函数的不同从而需要判断 UseShellExecute 属性来决定调用哪个方法
public partial class Process : IDisposable
{
... // 忽略其他代码
private bool StartCore(ProcessStartInfo startInfo)
{
return startInfo.UseShellExecute
? StartWithShellExecuteEx(startInfo)
: StartWithCreateProcess(startInfo);
}
... // 忽略其他代码
}
先来看看 StartWithCreateProcess 方法吧,这个方法比较简单,省略的代码如下
public partial class Process : IDisposable
{
... // 忽略其他代码
private unsafe bool StartWithCreateProcess(ProcessStartInfo startInfo)
{
if (startInfo.UserName.Length != 0)
{
... // 忽略其他代码
retVal = Interop.Advapi32.CreateProcessWithLogonW(
startInfo.UserName,
startInfo.Domain,
(passwordPtr != IntPtr.Zero) ? passwordPtr : (IntPtr)passwordInClearTextPtr,
logonFlags,
null, // we don't need this since all the info is in commandLine
commandLinePtr,
creationFlags,
(IntPtr)environmentBlockPtr,
workingDirectory,
ref startupInfo, // pointer to STARTUPINFO
ref processInfo // pointer to PROCESS_INFORMATION
);
}
else
{
... // 忽略其他代码
retVal = Interop.Kernel32.CreateProcess(
null, // we don't need this since all the info is in commandLine
commandLinePtr, // pointer to the command line string
ref unused_SecAttrs, // address to process security attributes, we don't need to inherit the handle
ref unused_SecAttrs, // address to thread security attributes.
true, // handle inheritance flag
creationFlags, // creation flags
(IntPtr)environmentBlockPtr, // pointer to new environment block
workingDirectory, // pointer to current directory name
ref startupInfo, // pointer to STARTUPINFO
ref processInfo // pointer to PROCESS_INFORMATION
);
}
... // 忽略其他代码
}
... // 忽略其他代码
}
在 dotnet 代码里面看到 StartWithCreateProcess 方法需要的代码很多,但其实只是调用 win32 方法比较繁琐而已
接下来看看 StartWithShellExecuteEx 方法的实现,通过这个方法的实现就可以知道为什么在 Windows 下,设置 UseShellExecute 为 true 且当调用线程非 STA 时,性能会较差的原因
public partial class Process : IDisposable
{
... // 忽略其他代码
private unsafe bool StartWithShellExecuteEx(ProcessStartInfo startInfo)
{
... // 忽略其他代码
ShellExecuteHelper executeHelper = new ShellExecuteHelper(&shellExecuteInfo);
if (!executeHelper.ShellExecuteOnSTAThread())
{
... // 忽略其他代码
}
... // 忽略其他代码
}
... // 忽略其他代码
}
可以看到在 StartWithShellExecuteEx 里使用的是 ShellExecuteHelper 辅助方法来实现,通过 ShellExecuteOnSTAThread 也能猜到,这是在 STA 线程执行的。这是因为启动线程如果是用来调用文件打开,一些 COM 是需要 STA 线程的。然而如果当前的线程不是 STA 的线程,那需要如何执行
接下来继续看 ShellExecuteOnSTAThread 的实现
internal unsafe class ShellExecuteHelper
{
... // 忽略其他代码
public bool ShellExecuteOnSTAThread()
{
// ShellExecute() requires STA in order to work correctly.
if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
{
ThreadStart threadStart = new ThreadStart(ShellExecuteFunction);
Thread executionThread = new Thread(threadStart)
{
IsBackground = true,
Name = ".NET Process STA"
};
executionThread.SetApartmentState(ApartmentState.STA);
executionThread.Start();
executionThread.Join();
}
else
{
ShellExecuteFunction();
}
... // 忽略其他代码
return _succeeded;
}
private void ShellExecuteFunction()
{
try
{
if (!(_succeeded = Interop.Shell32.ShellExecuteExW(_executeInfo)))
ErrorCode = Marshal.GetLastWin32Error();
}
catch (EntryPointNotFoundException)
{
_notpresent = true;
}
}
... // 忽略其他代码
}
可以看到在 dotnet 里面,判断当前线程,如果不是 STA 线程,那就再启动一个 STA 线程去执行代码,而且是等待启动的 STA 线程执行完成再方法,同步等待另一个线程。这就是为什么性能比较差的原因,性能差在需要启动线程和等待线程执行完成
那有伙伴说,那是不是每次都放在客户端的 STA 主线程调用好了,这样就让 dotnet 底层不需要启动新的线程?其实这不好,因为 ShellExecuteExW 这个 win32 方法不是非常快速的,在一些系统上,将会等待很长时间,特别是有 360 等的情况,如果在主线程被进入等待,那自然是不如多开一个后台线程
看完了原理之后,相信大家也就知道,如果明确知道是启动一个进程,只是启动时传传参数等,且没有其他的需求,可以放心设置 UseShellExecute 为 false 的值,当然,为 false 也是默认值,这样性能会更高
dotnet 6 创建进程 Process.Start 时设置 UseShellExecute 在 Windows 下对性能的影响的更多相关文章
- 设置 Quick-Cocos2d-x 在 Windows 下的编译环境
http://cn.cocos2d-x.org/tutorial/show?id=1304 设置 Quick-Cocos2d-x 在 Windows 下的编译环境 Liao Yulei2014-08- ...
- 同步异步、mutiprocessing创建进程process模块及进程对象的多种方法、消息队列Queue
目录 同步异步 阻塞与非阻塞 综合使用 创建进程的多种方式之multiprocess.process模块 进程间数据隔离 进程的join方法 IPC机制 生产者 消费者模型 进程对象的多种方法 守护进 ...
- wampserver-----------如何设置wampserver在windows下开机自动启动。
虽然很简单,但是还是做个记录.我的习惯,还是看图: 到你电脑的服务里面找到这两项然后点击右键属性,设置为自动.
- SQL Server在执行SQL语句时,表之间驱动顺序对性能的影响
环境:SQL Server2012 SP3 企业版,开发服务器,并没有什么负载,全库索引统一Rebuild过 经反复执行验证过, 不算太复杂的SQL(存储过程中代入参数抠出来的SQL代码) 默认情况下 ...
- windows下mongodb基础玩法系列二CURD操作(创建、更新、读取和删除)
windows下mongodb基础玩法系列 windows下mongodb基础玩法系列一介绍与安装 windows下mongodb基础玩法系列二CURD操作(创建.更新.读取和删除) windows下 ...
- Linux和Windows下的进程管理总结
在Windows和Linux下都可以很方便地列出当前运行的进程.Windows下可以使用组合键CTRL+ALT+DEL打开任务管理器,在进程选项卡中就列举出了当前运行的所有进程,除此之外还可以在命令行 ...
- 一起talk C栗子吧(第一百三十三回:C语言实例--创建进程时的内存细节)
各位看官们.大家好,上一回中咱们说的是从内存角度看进程和线程的样例.这一回咱们说的样例是:创建进程时的内存细节.闲话休提,言归正转.让我们一起talk C栗子吧! 看官们.我们都知道使用fork函数能 ...
- 使用Process子类创建进程
#_author:来童星#date:2019/12/17# 使用Process子类创建进程from multiprocessing import Processimport timeimport os ...
- 阿里巴巴Java开发手册建议创建HashMap时设置初始化容量,但是多少合适呢?
集合是Java开发日常开发中经常会使用到的,而作为一种典型的K-V结构的数据结构,HashMap对于Java开发者一定不陌生. 关于HashMap,很多人都对他有一些基本的了解,比如他和hashtab ...
- Java并发编程:如何创建进程?
转载自:http://www.cnblogs.com/dolphin0520/p/3913517.html 在前面一篇文章中已经讲述了在进程和线程的由来,今天就来讲一下在Java中如何创建线程,让线程 ...
随机推荐
- PyQt5 GUI编程
一.PyQt5简介 PyQt5是一个用于创建图形用户界面(GUI)应用程序的跨平台工具集,它将Qt库(广泛用于C++编程语言中创建丰富的GUI应用程序)的功能包装给Python使用者.PyQt5是由R ...
- 深度观察2024中国系统架构师大会(SACC)
今年的中国系统架构师大会(SACC)在我所在的城市广州举办,很荣幸受邀参加.这次能接触到国内最优秀的架构师,学习他们的架构思想和行业经验.对我而言非常有意义. 大会分为上下午共4场,我参加了上午的多云 ...
- Cesium之原生控件
1. 控件概述 Cesium的开始,基本上是从new一个Viewer开始 // ... <div id="cesiumContainer"></div> & ...
- C++ 自动计时
#include<iostream> #include<chrono> struct Timer { std::chrono::time_point<std::chron ...
- Java也可以像python般range出连续集合
Java lamada:IntStream --range(int startInclusive, int endExclusive):前包后不包 IntStream.range(0, 10).for ...
- #分治NTT,容斥定理,排列组合#LOJ 6503 「雅礼集训 2018 Day4」Magic
题目 桌面上摆放着 \(m\) 种魔术卡,共 \(n\) 张,第 \(i\) 种魔术卡数量为 \(a_i\),魔术卡顺次摆放,形成一个长度为 \(n\) 的魔术序列, 在魔术序列中,若两张相邻魔术卡的 ...
- 带你玩转OpenHarmony AI:基于Seetaface2的人脸识别
简介 随着时代的进步,全民刷脸已经成为一种新型的生活方式,这也是全球科技进步的又一阶梯,人脸识别技术已经成为一种大趋势,无论在智慧出行.智能家居.智慧办公等场景均有较广泛的应用场景,本文介绍了基于Se ...
- JMeter接口性能测试工具
博客地址:https://blog.csdn.net/lovesoo/article/details/78579547
- scala 生成指定日期范围的list
可以通过scala中的流处理,生成指定范围内的日期list import java.time.LocalDate def dateStream(fromDt:LocalDate):Stream[Loc ...
- c# webapi swagger
如何配置swagger? 在使用项目中,我们希望去查看我们的webapi的测试,那么我们是需要去有一个集成的测试的. 步骤 1.在nutget管理包中下载swagger包. 2.这样会在App_sta ...