1) 您有一些访问磁盘的 IO 密集型进程。
2) 您的 UI 在此阶段变得无响应。
3) 您的 UI
还有另一个要求,显示动画图像,而那些
进程正在运行。
更新:
4) 测试表明主要问题是缺少物理内存,这迫使系统交换到页面文件,同时发出新的顺序 IO 绑定准并发请求。
这会导致卡顿。
我建议使用以下方法将 UI 从 IO 进程中分离出来,使其无响应。
首先对其进行测试,然后运行您的实际任务。
更新:
在当前情况下,仅靠 Async 方法无法解决问题。
UI 卡顿是因为系统卡顿了。
因此,新提出的解决方案:
使用一段(或多段)虚拟内存访问大文件。
这是使用MemoryMappedFile 实现的,它将文件和内存空间关联起来,让映射的部分被视为主内存。
文件读取和写入操作以相同的方式执行,但通过MemoryMappedViewAccessor(随机访问)或MemoryMappedViewStream(顺序访问)。
当相关的Accessor为Flushed()时,执行一次写操作
可以共享访问器。进程可以在没有并发的情况下访问同一个 Accessor。
MyTaskResults 类用于存储结果并将参数传递给负责 IO 绑定进程的异步方法。
编辑1:
返回值必须是List<string> (?)
(为什么在返回的 List 上使用.FirstOrDefault())?
编辑 3:
向主类添加了两个共享的 MemoryMappedFile 对象。
public class MyTaskResults
{
public int TaskID { get; set; }
public string TextDirPath { get; set; }
public string ImagesDirPath { get; set; }
public string NativesDirPath { get; set; }
public List<string> TextDirPathResult { get; set; }
public List<string> ImagesDirPathResult { get; set; }
public List<string> NativesDirPathResult { get; set; }
}
//List of MyTaskResults Class, used to store all Tasks run and their results
List<MyTaskResults> _ListOfTasks = new List<MyTaskResults>();
private static MemoryMappedFile _mmfDatData;
private static MemoryMappedFile _mmfOptData;
bool CriticalJobRunning = false;
int _TasksCounter = 0;
您可以通过任何其他方法运行 TaskRunProxy(),而不必是异步方法。
编辑2:
在MainWindow.Loaded() 事件处理程序中将调用移至TaskRunProxy()
private void wMain_Loaded(object sender, RoutedEventArgs e) { TaskRunProxy(); }
添加了TextDirPath、ImagesDirPath、NativesDirPath 的搜索目录。
结果:
文本文件:765 (*.txt) - 图像文件:697 (*.jpg) - 本机文件:28422 (*.dll)
经过时间:88428 毫秒
初始:页面文件大小:4096 内存(工作集):60.907.520
最终:页面文件大小:4096 内存(工作集):91.385.856
总计:找到与提供的模式匹配的 29884 个文件。
总内存:30.478.336 (~29Mb)
用户界面甚至没有注意到它。
=> 内存已用完,系统可能会进行大量交换。应在清零-重启-重建系统页面文件(以及一般清理/碎片整理)之后执行测试。
编辑3:
创建 2 个内存映射文件,将大磁盘文件与虚拟内存空间相关联:
public MainWindow()
{
InitializeComponent();
string _datFilePath = @"PATHTOLARGEFILE";//~200MB
string _optFilePath = @"PATHTOLARGEFILE2";//~200MB
Int64 _sizeDatData = new FileInfo(_datFilePath).Length;
Int64 _sizeOptData = new FileInfo(_optFilePath).Length;
//Capacity = 0 means a capacity equal to the full size of the file on disk.
//Or _sizeDatData and _sizeOptData can be used.
_mmfDatData = MemoryMappedFile.CreateFromFile(_datFilePath,
FileMode.Open,
"DatData", 0,
MemoryMappedFileAccess.ReadWrite);
_mmfOptData = MemoryMappedFile.CreateFromFile(_optFilePath,
FileMode.Open,
"OptData", 0,
MemoryMappedFileAccess.ReadWrite);
}
读写操作:(随机访问)
[TYPE] 可以是引用类型或值类型(当然包括结构)
该示例将前 128 MB 用于读/写操作。
MemoryMappedViewAccessor _viewOptData = _mmfOptData.CreateViewAccessor(
0,
0x8000000L,
MemoryMappedFileAccess.ReadWrite);
_viewOptData.Read<[TYPE]>([Position], out [TYPE]);
_viewOptData.Write<[TYPE]>([Position], ref [TYPE]);
呈现 UI 后运行 IO 绑定任务。
private void wMain_Loaded(object sender, RoutedEventArgs e)
{
TaskRunProxy();
}
private async void TaskRunProxy()
{
_TasksCounter += 1;
MyTaskResults _Task = new MyTaskResults
{
TaskID = _TasksCounter,
TextDirPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
ImagesDirPath = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures),
NativesDirPath = Environment.GetFolderPath(Environment.SpecialFolder.Windows)
};
Console.WriteLine("Page File Size: " + Environment.SystemPageSize.ToString());
Console.WriteLine("Memory (Working Set): " + Environment.WorkingSet.ToString());
Stopwatch _SW = new Stopwatch();
_SW.Start();
CriticalJobRunning = true;
_ListOfTasks.Add(await GetFileListsAsync(_Task));
CriticalJobRunning = false;
_SW.Stop();
Console.WriteLine("Time: " + _SW.ElapsedMilliseconds + Environment.NewLine);
Console.WriteLine("TextFiles: " + _Task.TextDirPathResult.Count +
" ImageFiles: " + _Task.ImagesDirPathResult.Count +
" NativeFiles: " + _Task.NativesDirPathResult.Count);
Console.WriteLine("Page File Size: " + Environment.SystemPageSize.ToString());
Console.WriteLine("Memory (Working Set): " + Environment.WorkingSet.ToString());
}
private void wMain_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (this.CriticalJobRunning)
e.Cancel = true;
//Let the use know
}
用于运行(可能是同步的)任务的异步方法:
Edit1:
返回值更改为List<string>
Edit2:
使用“真实”文件枚举 + 内存加载更改了虚拟 Thread.Sleep(x):
Directory.GetFiles(_Task.[PATH], "[PATTERN]", SearchOption.AllDirectories).ToList<string>();
private async Task<MyTaskResults> GetFileListsAsync(MyTaskResults _Task)
{
if (!string.IsNullOrEmpty(_Task.TextDirPath))
_Task.TextDirPathResult = await Task.Run(() =>
{
return Directory.GetFiles(_Task.TextDirPath,
"*.txt",
SearchOption.AllDirectories).ToList<string>();
//Thread.Sleep(4000);
//return new List<string> {TextDirPathResult Completed"};
//return FileList.GetFileList(_Task.TextDirPath);
});
if (!string.IsNullOrEmpty(_Task.ImagesDirPath))
_Task.ImagesDirPathResult = await Task.Run(() =>
{
return Directory.GetFiles(_Task.ImagesDirPath,
"*.jpg",
SearchOption.AllDirectories).ToList<string>();
//Thread.Sleep(3000);
//return new List<string> {"TextDirPathResult Completed"};
//return FileList.GetFileList(_Task.ImagesDirPath);
});
if (!string.IsNullOrEmpty(_Task.NativesDirPath))
_Task.NativesDirPathResult = await Task.Run(() =>
{
return Directory.GetFiles(_Task.NativesDirPath,
"*.dll",
SearchOption.AllDirectories).ToList<string>();
//Thread.Sleep(3000);
//return new List<string> {"TextDirPathResult Completed"};
//return FileList.GetFileList(_Task.NativesDirPath);
});
return _Task;
}