我正在开发一个应用程序(.NET 4.0,C#):
1.扫描文件系统。
2.打开并读取一些文件。
该应用程序将在后台运行,对磁盘使用率影响较小。如果用户正在执行常规任务并且磁盘使用率很高,则不应该打扰用户。反之亦然,如果没有人使用磁盘,应用程序可以更快 主要问题是由于使用API(mapi32.dll)来读取文件,我不知道I / O操作的实际数量和大小。如果我要求API做某事我不知道它读取多少字节来处理我的响应。
所以问题是如何监控和管理磁盘使用情况?包括文件系统扫描和文件读取......
检查标准性能监视器工具使用的性能计数器?还是其他任何方式?
答案 0 :(得分:19)
使用System.Diagnostics.PerformanceCounter
类,附加到与要编制索引的驱动器相关的PhysicalDisk计数器。
下面是一些代码来说明,虽然它目前硬编码为“C:”驱动器。您需要将“C:”更改为您的进程扫描的任何驱动器。 (这是粗略的示例代码,仅用于说明性能计数器的存在 - 不要将其视为提供准确信息 - 应始终仅用作指南。根据您自己的目的进行更改)
观察%空闲时间计数器,该计数器指示驱动器执行任何操作的频率。 0%空闲表示磁盘正忙,但并不一定意味着磁盘已耗尽,无法传输更多数据。
将%空闲时间与当前磁盘队列长度结合使用,这将告诉您驱动器是否正忙着无法为所有请求提供服务数据。作为一般准则,任何超过0意味着驱动器可能是平坦的忙碌而超过2意味着驱动器完全饱和。这些规则适用于SSD和HDD。
此外,您读取的任何值都是某个时间点的瞬时值。你应该在几个结果上做一个平均值,例如在使用结果中的信息做出决定之前,每100毫秒读取一次读数并平均读取5次(即,在下一次IO请求之前等待计数器结算)。
internal DiskUsageMonitor(string driveName)
{
// Get a list of the counters and look for "C:"
var perfCategory = new PerformanceCounterCategory("PhysicalDisk");
string[] instanceNames = perfCategory.GetInstanceNames();
foreach (string name in instanceNames)
{
if (name.IndexOf("C:") > 0)
{
if (string.IsNullOrEmpty(driveName))
driveName = name;
}
}
_readBytesCounter = new PerformanceCounter("PhysicalDisk",
"Disk Read Bytes/sec",
driveName);
_writeBytesCounter = new PerformanceCounter("PhysicalDisk",
"Disk Write Bytes/sec",
driveName);
_diskQueueCounter = new PerformanceCounter("PhysicalDisk",
"Current Disk Queue Length",
driveName);
_idleCounter = new PerformanceCounter("PhysicalDisk",
"% Idle Time",
driveName);
InitTimer();
}
internal event DiskUsageResultHander DiskUsageResult;
private void InitTimer()
{
StopTimer();
_perfTimer = new Timer(_updateResolutionMillisecs);
_perfTimer.Elapsed += PerfTimerElapsed;
_perfTimer.Start();
}
private void PerfTimerElapsed(object sender, ElapsedEventArgs e)
{
float diskReads = _readBytesCounter.NextValue();
float diskWrites = _writeBytesCounter.NextValue();
float diskQueue = _diskQueueCounter.NextValue();
float idlePercent = _idleCounter.NextValue();
if (idlePercent > 100)
{
idlePercent = 100;
}
if (DiskUsageResult != null)
{
var stats = new DiskUsageStats
{
DriveName = _readBytesCounter.InstanceName,
DiskQueueLength = (int)diskQueue,
ReadBytesPerSec = (int)diskReads,
WriteBytesPerSec = (int)diskWrites,
DiskUsagePercent = 100 - (int)idlePercent
};
DiskUsageResult(stats);
}
}
答案 1 :(得分:3)
需要思考的问题:如果有其他流程遵循相同(或类似)策略会怎么样?哪一个会在“空闲时间”运行?其他进程是否有机会利用空闲时间?
显然,除非有一些众所周知的操作系统机制在空闲时间内公平地划分资源,否则无法正确完成。在Windows中,这可以通过调用SetPriorityClass来完成。
这document about I/O prioritization in Vista似乎意味着IDLE_PRIORITY_CLASS
不会真正降低I / O请求的优先级(尽管它会降低进程的调度优先级)。 Vista为此添加了新的PROCESS_MODE_BACKGROUND_BEGIN
和PROCESS_MODE_BACKGROUND_END
值。
在C#中,您通常可以使用Process.PriorityClass属性设置进程优先级。但是Vista的新值不可用,因此您必须直接调用Windows API函数。你可以这样做:
[DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool SetPriorityClass(IntPtr handle, uint priorityClass);
const uint PROCESS_MODE_BACKGROUND_BEGIN = 0x00100000;
static void SetBackgroundMode()
{
if (!SetPriorityClass(new IntPtr(-1), PROCESS_MODE_BACKGROUND_BEGIN))
{
// handle error...
}
}
我没有测试上面的代码。不要忘记它只适用于Vista或更好。您必须使用Environment.OSVersion
来检查早期的操作系统并实施后备策略。
答案 2 :(得分:2)
很久以前,微软研究院就此发表了一篇论文(对不起,我记不起网址了) 我记得:
基本理想是:
“如果他们放慢我的速度,那么我 必须放慢速度,所以少做 如果我放慢速度,我会工作“
答案 3 :(得分:1)
有关相关查询,请参阅this question和this also。我建议一个简单的解决方案只是查询当前的磁盘和放大器。 CPU使用率%经常出现,并且仅在当前任务低于定义的阈值时继续使用。只需确保您的工作很容易被分解成任务,并且每项任务都可以轻松完成。有效地开始/停止。
答案 4 :(得分:0)
检查屏幕保护程序是否正在运行?用户远离键盘的良好指示