应用程序最大内存

时间:2018-01-18 17:03:52

标签: c# memory

我有一个将文件读入内存的应用程序。然后,在将结果呈现给用户之前,它会对此数据进行大量检查(检查在后台同时在多个任务上运行)。

虽然应用程序没有因OutOfMemory异常而崩溃,但我注意到即使处理在后台进行,UI也会挂起,窗口会突然变为“Not Responding”。应用程序最终将完成任务并显示结果,但同时应用程序看起来已损坏,并会提示用户关闭窗口。

经过大量谷歌搜索后,我不确定如何处理这个问题。有什么我应该做的,以确保应用程序不会变得没有反应?我是否应该尝试计算启动时估计的内存可用性与使用情况,并在机器没有足够RAM时提醒用户?如果可用内存低于一定数量,我应该监控内存使用情况吗?

为了清楚起见,所有处理都在后台进行。我正在使用Task.Run。我想我可以告诉UI线程没有被处理阻止,因为当我在具有足够内存的UI上运行时,UI不会出现问题。

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    TaskList();
}
private async Task TaskList()
{
var taskList = new List<Task>();
txbStatus.Text += "Processing...\n";

taskList.Add(Task.Run(async () =>
{
        await GetFileLists();

    Application.Current.Dispatcher.Invoke(() => { txbStatus.Text += "Get file list completed\n"; });
}));

await Task.WhenAll(taskList.ToArray());
}
private async Task GetFileLists()
{
    var taskList = new List<Task>();

    if (!string.IsNullOrEmpty(TextDirPath))
        taskList.Add(Task.Run(() =>
        {
            _textFilesInFolderPathList =
                FileList.GetFileList(TextDirPath);
        }));
    if (!string.IsNullOrEmpty(ImagesDirPath))
        taskList.Add(Task.Run(() =>
        {
            _imageFilesInFolderPathList =
                FileList.GetFileList(ImagesDirPath));
        }));

    if (!string.IsNullOrEmpty(NativesDirPath))
        taskList.Add(Task.Run(() =>
        {
            _nativesFilesInFolderPathList =
                FileList.GetFileList(NativesDirPath);
        }));

    await Task.WhenAll(taskList.ToArray());
}

屏幕截图

enter image description here enter image description here

1 个答案:

答案 0 :(得分:0)

  

1)您有一些访问磁盘的IO密集型进程   2)在此阶段,您的用户界面无响应。
3)您的用户界面   有另一个要求,以显示动画图像   进程正在运行   的更新
  4)测试表明主要问题是缺少物理内存,这会强制系统交换到页面文件,而新的顺序IO绑定准并发请求。
这会导致口吃。

我建议使用以下方法将UI与IO进程分离,使其无响应 首先测试它,然后运行您的实际任务 的更新
在目前的情况下,单独使用异步方法无法解决问题 用户界面因为系统而口吃
因此,新提出的解决方案:
使用一段(或多段)虚拟内存访问大文件 这是使用MemoryMappedFile来实现的,它将文件和内存空间相关联,让映射的部分被视为主内存。 文件读取和写入操作以相同的方式执行,但通过MemoryMappedViewAccessor(随机访问)或MemoryMappedViewStream(顺序访问)执行。 当相关的访问者为Flushed()时,执行写操作 可以共享访问者。进程可以在没有并发的情况下访问同一个Accessor。

类MyTaskResults用于存储结果并将参数传递给负责IO绑定进程的异步方法。

修改1:
返回的值必须是List<string>(?)
           (为什么在返回的列表上使用.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(),这不一定是异步方法。

<强> EDIT2:
将来电移至TaskRunProxy()事件处理程序中的MainWindow.Loaded()

private void wMain_Loaded(object sender, RoutedEventArgs e) { TaskRunProxy(); }

添加了TextDirPathImagesDirPathNativesDirPath的搜索目录。
结果:

  

TextFiles:765(*.txt) - ImageFiles:697(*.jpg) - NativeFiles:28422(*.dll
  
  经历时间:88428ms
  初始:页面文件大小:4096内存(工作集):60.907.520
  最终:页面文件大小:4096内存(工作集):91.385.856   

  总计:找到29884个与提供的模式匹配的文件。
  总内存:30.478.336(~29Mb)
  
  用户界面甚至没有注意到它。
  =&GT;使用最大内存时,系统可能会大量交换。归零 - 重启 - 重建系统页面文件(以及常规清理/碎片整理)后,应该进行测试。

<强> EDIT3:
创建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
}

用于运行(可能是同步的)任务的异步方法:

编辑1:
返回值已更改为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;
}