当前位置: 首页 > 知识库问答 >
问题:

IO绑定的操作和任务。Run()

易骁
2023-03-14

我对并发(实际上是C#)很陌生。我在两个单独的目录中有一堆csv文件要读取,然后我想在读取文件后进行一些处理。该处理独立于其他数据读取和处理操作。完成所有处理后,我想更新UI。用户界面同时也需要响应,因为我需要显示一个进度条。目前我有这样的想法:

private string _directoryA;
private string _directoryB;

// The user clicks the button
private void ButtonPressed()
{
    Task.Run(() => DoJob());
} 

private void DoJob()
{
    var tasks = new List<Task>();
    var watch = Stopwatch.StartNew();

    tasks.Add(Task.Run(() => DoJobForDirectory(_directoryA).ContinueWith(t => Console.WriteLine("First Half");
    tasks.Add(Task.Run(() => DoJobForDirectory(_directoryB).ContinueWith(t => Console.WriteLine("Second Half");
    
    Task.WaitAll(tasks.ToArray());

    watch.Stop();
    Console.WriteLine($"Time Taken : {watch.ElapsedMilliseconds} ms.");
    UpdateUI();
}

private void DoJobForDirectory(string directory)
{
    var files = Directory.EnumerateFiles(directory, "*.csv");
    var tasks = new List<Task>();
    foreach (var file in files)
    {
        // Update the progress bar in the UI when a file has finished processing
        tasks.Add(Task.Run(() => DoJobForFile(file)).ContinueWith(t => UpdateCounter++));
    }

    Task.WaitAll(tasks.ToArray());
}

private void DoJobForFile(string filePath)
{
    ReadCSV();
    ProcessData();
    ...
}

我觉得我错过了什么。从我的阅读来看,这个操作应该是I/O绑定的,因为之后的处理相当轻量级(有些用于循环和分配)。所以我真的应该只使用async await,而不是Task.Run()。。。?然而,我想不出比这更好的方法了。ReadCSV()来自一些没有异步版本的库。使用并行。ForEach也不能提高性能。有没有更好的方法来做到这一点(高效利用资源并实现更好的性能)?

此外,当我尝试只在一个目录上运行时,所花费的时间将是两个目录所需时间的近一半。由于操作都是独立的,我想将它们全部并行运行,因此处理两个目录所花费的时间应该与处理单个目录所花费的时间大致相同(或仅略多),但速度不会慢两倍。似乎无论有多少任务。Run()我做了,我将有有限数量的线程同时运行(一些瓶颈)。我尝试将所有任务更改为new Thread(),并观察到更多线程同时处于活动状态,但最终导致性能更差。那是为什么呢?

共有2个答案

白淇
2023-03-14

任务。运行(()=

我将其更改为:

private string _directoryA;
private string _directoryB;

// The user clicks the button
private async void ButtonPressed()
{
    // disable UI controls
    try
    {
        await DoJob();
    }
    finally
    {
        // enable UI controls.
    }
} 

private async Task DoJob()
{
    var tasks = new List<Task>();
    var watch = Stopwatch.StartNew();

    tasks.Add(Task.Run(async () => DoJobForDirectory(_directoryA).ContinueWith(t => Console.WriteLine("First Half");
    tasks.Add(Task.Run(async () => DoJobForDirectory(_directoryB).ContinueWith(t => Console.WriteLine("Second Half");
    
    await Task.WhenAll(tasks.ToArray());

    watch.Stop();
    Console.WriteLine($"Time Taken : {watch.ElapsedMilliseconds} ms.");
    UpdateUI();
}

private async Task DoJobForDirectory(string directory)
{
    var files = Directory.EnumerateFiles(directory, "*.csv");
    var tasks = new List<Task>();
    foreach (var file in files)
    {
        // Update the progress bar in the UI when a file has finished processing
        tasks.Add(Task.Run(() => DoJobForFile(file)).ContinueWith(t => UpdateCounter++));
    }
    
    await Task.WhenAll(tasks.ToArray());
}

private void DoJobForFile(string filePath)
{
    ReadCSV();
    ProcessData();
    ...
}

如果要限制线程,可以使用信号量限制。下面是一个公认答案的好例子:

如何限制c#中并行任务的最大数量

斜宁
2023-03-14

任务。运行计划在ThreadPool上工作,这是一种保守的机制,涉及它立即按需创建多少线程(它创建的线程与机器的可用内核一样多),以及当工作需求很高时创建新线程的频率(每秒一个新线程)。您可以尝试使用ThreadPool。SetMinThread方法,影响ThreadPool的行为。例如:

ThreadPool.SetMinThreads(100, 100);

这样,线程池将根据需要立即创建100个线程,然后再切换到保守算法。

您很可能看不到目录处理应用程序的性能有所提高。这是因为您的I/O绑定工作负载受到存储设备功能的限制。无论您使用代码做什么,硬件对每个时间单位可以存储或检索多少数据都有限制。当您达到此限制时,提高性能的唯一方法是升级硬件。

关于使用任务的适用性。运行和同步API来完成I/O绑定的工作,令人惊讶的是,在许多情况下,这是完成工作的最有效方式。特别是同步文件系统API比异步API快得多。同步API损失的是内存效率。每个线程的堆栈至少需要1 MB的内存,因此如果您一次启动1,000个线程,您的系统将失去1 GB或更多的内存,这可能会间接对应用程序的性能产生负面影响。

使用任务手动启动任务。为了并行化的目的,Run是并行化工作的一种低级方法。TPL提供了更高级别的基于任务的工具,如并行类、PLINQ库(.aspallel)和TPL数据流库。

为了在后台工作期间用进度信息更新用户界面,现代方法是i进程

 类似资料:
  • 主要内容:numpy.save(),savetxt()NumPy  IO 操作是以文件的形式从磁盘中加载 ndarray 对象。在这个过程中,NumPy 可以两种文件类型处理 ndarray 对象,一类是二进制文件(以 结尾),另一类是普通文本文件。 上述两种文件格式,分别对应着不同的 IO 方法,如下所示: NumPy IO操作方法 文件类型 处理方法 二进制文件 load() 和 save() 普通文本文件 loadtxt() 和 savetxt

  • ndarray 对象可以保存到磁盘文件并从磁盘文件加载。常用的 IO 函数有: load() 和 save() 函数是读写文件数组数据的两个主要函数,默认情况下,数组是以未压缩的原始二进制格式保存在扩展名为 .npy 的文件中; savez() 函数用于将多个数组写入文件,默认情况下,数组是以未压缩的原始二进制格式保存在扩展名为 .npz 的文件中; loadtxt() 和 savetxt() 函

  • 我有一个JavaFX 8应用程序,希望允许一个任务修改两个不同的UI元素。据我所知,如果我有一个标签要修改,我可以用mylabel绑定到标签。textProperty()。绑定(mytask.messageProperty())并在任务中使用updateMessage()。 如何使用两种不同的任意类型执行此操作?我已经查看了并发和JavaFX文档中的示例,但对我来说,它们并没有很好地解释这一点。

  • JavaFX 8任务和服务之间有什么区别,在哪种情况下使用一个比另一个更好?数据库操作用什么比较好?

  • 本文向大家介绍vue中v-model对select的绑定操作,包括了vue中v-model对select的绑定操作的使用技巧和注意事项,需要的朋友参考一下 1、单选时 如果 v-model表达式的value初始值未能匹配任何选项,<select> 元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS 不会触发 change 事件。因此,更推荐像上面这

  • 1. 打开和关闭文件 1.1 打开文件 访问文件前,需要使用用 Python 内置的 open() 函数打开一个文件: open(path, access_mode) path 是要访问的文件的路径名 access_mode 是文件的访问模式 可以是只读、读写、追加等模式,所有可能的取值见 1.2 小节 这个参数是可选的,缺省情况下,是以只读模式 r 打开文件 open 返回一个 file 对象