长时间IO极限耗尽硬盘时应自定义队列依次执行

 

当需要对磁盘做长时间高速IO时,为了避免IO争用造成大量时间用于寻道而导致吞吐量降低,以及长时间IO造成错误数超限导致磁盘进入降速模式。在这种极限耗尽磁盘性能的情况下,应当把磁盘当做只能做单个任务的串行设备来用,对所有IO自行控制,排队依次执行,不能过分相信操作系统能把该问题解决好。

 

  1 using System;
  2 using System.Collections.Concurrent;
  3 using System.Collections.Generic;
  4 using System.Diagnostics;
  5 using System.IO;
  6 using System.Threading;
  7 using Timer = System.Timers.Timer;
  8 
  9 namespace FileMultiWriter
 10 {
 11     public enum DiskLargeIOBackgroudServiceAddRetrunCode
 12     {
 13         Succeed,
 14         Fail,
 15         FilePathNotInDisk,
 16         ServiceStopping
 17     }
 18 
 19     public enum IOType
 20     {
 21         Read,
 22         Write
 23     }
 24 
 25     public class DiskLargeIOBackgroudService
 26     {
 27         private const int TimeSliceReserveForOtherIO = 1000;
 28         private volatile bool isStopping = false;
 29         private volatile bool isRunning = false;
 30         private object ioLock = new object();
 31         private ConcurrentQueue<Tuple<IOType, string, byte[], Action<object>>> queue = new ConcurrentQueue<Tuple<IOType, string, byte[], Action<object>>>();
 32         private Timer timer;
 33         private Dictionary<char, bool> diskLogicDrives = new Dictionary<char, bool>();
 34 
 35         public DiskLargeIOBackgroudService(char[] logicDriveLabels, double checkIterval)
 36         {
 37             foreach (var logicDriveLabel in logicDriveLabels)
 38             {
 39                 diskLogicDrives[logicDriveLabel] = true;
 40             }
 41             timer = new Timer(checkIterval);
 42             timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
 43             timer.Start();
 44 
 45             SetIOPriorityToIdle();
 46         }
 47 
 48         #region public method
 49 
 50         public DiskLargeIOBackgroudServiceAddRetrunCode Enqueue(IOType ioType, string path, byte[] data, Action<object> callback)
 51         {
 52             if (isStopping) return DiskLargeIOBackgroudServiceAddRetrunCode.ServiceStopping;
 53             if (!CheckPath(path)) return DiskLargeIOBackgroudServiceAddRetrunCode.FilePathNotInDisk;
 54 
 55             Tuple<IOType, string, byte[], Action<object>> item = new Tuple<IOType, string, byte[], Action<object>>(ioType, path, data, callback);
 56             queue.Enqueue(item);
 57             return DiskLargeIOBackgroudServiceAddRetrunCode.Succeed;
 58         }
 59 
 60         public void Stop()
 61         {
 62             isStopping = true;
 63             while (!queue.IsEmpty)
 64             {
 65                 Thread.Sleep(1000);
 66             }
 67             timer.Stop();
 68             timer.Elapsed -= timer_Elapsed;
 69 
 70         }
 71 
 72         #endregion
 73 
 74         #region private method
 75 
 76         private void SetIOPriorityToIdle()
 77         {
 78             Process process = Process.GetCurrentProcess();
 79             IntPtr hProcess = process.Handle;
 80 
 81             ApiHelper.SetIOPriority(hProcess, IoPriority.Idle);
 82         }
 83 
 84         private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
 85         {
 86             CheckQueue();
 87         }
 88 
 89         private bool CheckPath(string path)
 90         {
 91             char drive = path.ToLower()[0];
 92             if (diskLogicDrives.ContainsKey(drive)) return true;
 93             return false;
 94         }
 95 
 96         private void CheckQueue()
 97         {
 98             if (isRunning) return;
 99 
100             if (queue.IsEmpty) return;
101 
102             if (TryAquireLock())
103             {
104                 SyncRun();
105             }
106         }
107 
108         private bool TryAquireLock()
109         {
110             bool result = false;
111             lock (ioLock)
112             {
113                 if (isRunning == false)
114                 {
115                     isRunning = true;
116                     result = true;
117                 }
118             }
119             return result;
120         }
121 
122         private void ReleaseLock()
123         {
124             lock (ioLock)
125             {
126                 isRunning = false;
127             }
128         }
129 
130         private void SyncRun()
131         {
132             try
133             {
134                 DiskOperation();
135             }
136             catch (Exception ex)
137             {
138                 throw ex;
139             }
140             finally
141             {
142                 ReleaseLock();
143             }
144         }
145 
146         private void DiskOperation()
147         {
148             Tuple<IOType, string, byte[], Action<object>> item;
149             while (!queue.IsEmpty)
150             {
151                 while (queue.TryDequeue(out item))
152                 {
153                     if (item.Item1 == IOType.Write)
154                     {
155                         Write(item);
156                     }
157                     else
158                     {
159                         Read(item);
160                     }
161                     Thread.Sleep(TimeSliceReserveForOtherIO);
162                 }
163             }
164         }
165 
166         private void Write(Tuple<IOType, string, byte[], Action<object>> item)
167         {
168             using (FileStream fs = new FileStream(item.Item2, FileMode.Create, FileAccess.Write))
169             {
170                 fs.Write(item.Item3, 0, item.Item3.Length);
171                 fs.Close();
172             }
173             Action<object> action = item.Item4;
174             action.BeginInvoke(item.Item2, null, null);
175         }
176 
177         private void Read(Tuple<IOType, string, byte[], Action<object>> item)
178         {
179             byte[] buf;
180             using (FileStream fs = new FileStream(item.Item2, FileMode.Open, FileAccess.Read))
181             {
182                 buf = new byte[fs.Length];
183                 int offset = 0;
184                 int segment = 1024*1024;
185                 while (offset < buf.Length)
186                 {
187                     int readNum = fs.Read(buf, offset, segment);
188                     offset += readNum;
189                 }
190                 fs.Close();
191             }
192             Action<object> action = item.Item4;
193             action.BeginInvoke(buf, null, null);
194         }
195 
196         #endregion
197     }
198 }




 

由于硬盘读写时默认是全速运行,为了避免长时间高速IO造成系统失去响应,应设置IO优先级为Idle。

 

  1 using System;
  2 using System.Runtime.InteropServices;
  3 
  4 namespace FileMultiWriter
  5 {
  6     public class ApiHelper
  7     {
  8         public static int SetIOPriority(IntPtr hProcess, IoPriority newPriority)
  9         {
 10             int sizeOfInt = 4;
 11             byte[] src = new byte[sizeOfInt];
 12             src[0] = (byte)newPriority;
 13             IntPtr priorityPointer = Marshal.AllocHGlobal(new IntPtr(sizeOfInt));
 14             Marshal.Copy(src, 0, priorityPointer, sizeOfInt);
 15             int result = ApiHelper.NtSetInformationProcess(hProcess, PROCESS_INFORMATION_CLASS.ProcessIoPriority, priorityPointer, (uint)sizeOfInt);
 16             Marshal.FreeHGlobal(priorityPointer);
 17             return result;
 18         }
 19 
 20         [DllImport("kernel32.dll")]
 21         public static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
 22       
 23         [DllImport("ntdll.dll", SetLastError = true)]
 24         public static extern int NtQueryInformationProcess(IntPtr processHandle, PROCESS_INFORMATION_CLASS processInformationClass, ref IntPtr processInformation, uint processInformationLength, out uint returnLength);
 25        
 26         [DllImport("ntdll.dll", SetLastError = true)]
 27         public static extern int NtSetInformationProcess(IntPtr processHandle, PROCESS_INFORMATION_CLASS processInformationClass, IntPtr processInformation, uint processInformationLength);
 28         
 29         [DllImport("KERNEL32.DLL")]
 30         public static extern IntPtr OpenProcess(PROCESS_RIGHTS dwDesiredAccess, bool bInheritHandle, int dwProcessId);
 31         
 32         [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
 33         public static extern bool SetPriorityClass(IntPtr handle, uint priorityClass);
 34     }
 35 
 36     [StructLayout(LayoutKind.Sequential)]
 37     public class SECURITY_ATTRIBUTES
 38     {
 39         public int nLength;
 40         public byte[] lpSecurityDescriptor;
 41         public int bInheritHandle;
 42     }
 43 
 44     public enum PROCESS_INFORMATION_CLASS
 45     {
 46         ProcessBasicInformation,
 47         ProcessQuotaLimits,
 48         ProcessIoCounters,
 49         ProcessVmCounters,
 50         ProcessTimes,
 51         ProcessBasePriority,
 52         ProcessRaisePriority,
 53         ProcessDebugPort,
 54         ProcessExceptionPort,
 55         ProcessAccessToken,
 56         ProcessLdtInformation,
 57         ProcessLdtSize,
 58         ProcessDefaultHardErrorMode,
 59         ProcessIoPortHandlers,
 60         ProcessPooledUsageAndLimits,
 61         ProcessWorkingSetWatch,
 62         ProcessUserModeIOPL,
 63         ProcessEnableAlignmentFaultFixup,
 64         ProcessPriorityClass,
 65         ProcessWx86Information,
 66         ProcessHandleCount,
 67         ProcessAffinityMask,
 68         ProcessPriorityBoost,
 69         ProcessDeviceMap,
 70         ProcessSessionInformation,
 71         ProcessForegroundInformation,
 72         ProcessWow64Information,
 73         ProcessImageFileName,
 74         ProcessLUIDDeviceMapsEnabled,
 75         ProcessBreakOnTermination,
 76         ProcessDebugObjectHandle,
 77         ProcessDebugFlags,
 78         ProcessHandleTracing,
 79         ProcessIoPriority,
 80         ProcessExecuteFlags,
 81         ProcessResourceManagement,
 82         ProcessCookie,
 83         ProcessImageInformation,
 84         ProcessCycleTime,
 85         ProcessPagePriority,
 86         ProcessInstrumentationCallback,
 87         ProcessThreadStackAllocation,
 88         ProcessWorkingSetWatchEx,
 89         ProcessImageFileNameWin32,
 90         ProcessImageFileMapping,
 91         ProcessAffinityUpdateMode,
 92         ProcessMemoryAllocationMode,
 93         MaxProcessInfoClass
 94     }
 95 
 96 
 97     public enum PROCESS_RIGHTS : uint
 98     {
 99         PROCESS_ALL_ACCESS = 0x1fffff,
100         PROCESS_CREATE_PROCESS = 0x80,
101         PROCESS_CREATE_THREAD = 2,
102         PROCESS_DUP_HANDLE = 0x40,
103         PROCESS_QUERY_INFORMATION = 0x400,
104         PROCESS_QUERY_LIMITED_INFORMATION = 0x1000,
105         PROCESS_SET_INFORMATION = 0x200,
106         PROCESS_SET_QUOTA = 0x100,
107         PROCESS_SET_SESSIONID = 4,
108         PROCESS_SUSPEND_RESUME = 0x800,
109         PROCESS_TERMINATE = 1,
110         PROCESS_VM_OPERATION = 8,
111         PROCESS_VM_READ = 0x10,
112         PROCESS_VM_WRITE = 0x20
113     }
114 
115     [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
116     public struct STARTUPINFO
117     {
118         public int cb;
119         public string lpReserved;
120         public string lpDesktop;
121         public string lpTitle;
122         public int dwX;
123         public int dwY;
124         public int dwXSize;
125         public int dwYSize;
126         public int dwXCountChars;
127         public int dwYCountChars;
128         public int dwFillAttribute;
129         public int dwFlags;
130         public short wShowWindow;
131         public short cbReserved2;
132         public IntPtr lpReserved2;
133         public IntPtr hStdInput;
134         public IntPtr hStdOutput;
135         public IntPtr hStdError;
136     }
137 
138     [StructLayout(LayoutKind.Sequential)]
139     public struct PROCESS_INFORMATION
140     {
141         public IntPtr hProcess;
142         public IntPtr hThread;
143         public int dwProcessId;
144         public int dwThreadId;
145     }
146 
147 
148     [StructLayout(LayoutKind.Sequential)]
149     public struct PROCESS_BASIC_INFORMATION
150     {
151         public int ExitStatus;
152         public int PebBaseAddress;
153         public int AffinityMask;
154         public int BasePriority;
155         public int UniqueProcessId;
156         public int InheritedFromUniqueProcessId;
157         public int Size
158         {
159             get
160             {
161                 return 0x18;
162             }
163         }
164     }
165 
166     //only 0 1 2 can be set in user mode, set to idle can prevent system no response
167     public enum IoPriority
168     {
169         Idle = 0,
170         Low = 1,
171         Normal = 2
172     }
173 }

 

 

测试代码:

 1 private static void WriteTest3()
 2         {
 3             DiskLargeIOBackgroudService service = new DiskLargeIOBackgroudService(new char[2] { 'c', 'd' }, 1000);
 4             string path = @"D:\down\tempData\";
 5 
 6             string teststring = "abcdefghijABCDEFGHIJ0123456789";
 7             for (int i = 0; i < 10; i++)
 8             {
 9                 byte[] mockArray = new byte[1024 * 1024 * 512];
10                 for (int j = 0; j < 1024 * 1024 * 500; j++)
11                 {
12                     mockArray[j] = (byte)teststring[i];
13                 }
14                 string filename = GenPath(path + i.ToString());
15                 service.Enqueue(IOType.Write, filename, mockArray, (x) => Console.WriteLine(x));
16                 service.Enqueue(IOType.Read, filename, null, x => PrintFile(x));
17             }
18 
19             Thread.Sleep(2000);
20             service.Stop();
21         }
22 
23         private static void PrintFile(object file)
24         {
25             byte[] array = (byte[]) file;
26             int offset = 0;
27             while (offset < array.Length)
28             {
29                 if(offset%(1024*1024)==0)
30                 Console.Write((char)array[offset]);
31                 offset++;
32             }
33             GC.Collect();
34         }

 

posted @ 2013-08-09 11:01  ^^!  阅读(663)  评论(1编辑  收藏  举报