如何限制.NET应用程序中的I / O操作?

时间:2010-10-25 10:49:45

标签: c# .net io limit

我正在开发一个应用程序(.NET 4.0,C#): 1.扫描文件系统。
2.打开并读取一些文件。

该应用程序将在后台运行,对磁盘使用率影响较小。如果用户正在执行常规任务并且磁盘使用率很高,则不应该打扰用户。反之亦然,如果没有人使用磁盘,应用程序可以更快 主要问题是由于使用API​​(mapi32.dll)来读取文件,我不知道I / O操作的实际数量和大小。如果我要求API做某事我不知道它读取多少字节来处理我的响应。

所以问题是如何监控和管理磁盘使用情况?包括文件系统扫描和文件读取......

检查标准性能监视器工具使用的性能计数器?还是其他任何方式?

5 个答案:

答案 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_BEGINPROCESS_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 questionthis also。我建议一个简单的解决方案只是查询当前的磁盘和放大器。 CPU使用率%经常出现,并且仅在当前任务低于定义的阈值时继续使用。只需确保您的工作很容易被分解成任务,并且每项任务都可以轻松完成。有效地开始/停止。

答案 4 :(得分:0)

检查屏幕保护程序是否正在运行?用户远离键盘的良好指示