gpt4 book ai didi

c# - 共享资源访问的优化同步

转载 作者:行者123 更新时间:2023-12-03 13:02:05 26 4
gpt4 key购买 nike

对于我的控制台应用程序,我实现了一个简单的文件记录器。记录器使用 StringBuilder继续追加日志条目并写入 LogText数据到LogFile在末尾。这导致只有一个文件 I/O 操作。应用程序执行必须非常快,因此,我实现了 Parallel.ForEach连同 async - await并尽可能减少 I/O 操作。
问题是 Logger不是线程安全的。使用 lockMonitor同步共享资源logger里面 Parallel.ForEach循环会降低性能。是否有任何最佳方式来同步不会对执行速度产生太大影响的共享资源?
我对替代方法或建议持开放态度。
记录器.cs

public class Logger
{
private readonly string LogFile;
private readonly StringBuilder LogText;

public Logger(string logFile)
{
LogFile = logFile;
LogText = new StringBuilder();
}

public void Write(string message)
{
LogText.AppendLine(message);
}

public void WriteToFile()
{
File.AppendAllText(LogFile, LogText.ToString());
}
}
程序.cs
public class Program
{
public static void Main(string[] args)
{
string logFile = args[0];
string workingDirectory = args[1];
Logger logger = new Logger(logFile);
logger.Write($"INFO | Execution Started");

try
{
List<string> files = Directory.EnumerateFiles(workingDirectory, "*", SearchOption.AllDirectories).ToList();
Parallel.ForEach(files, async file =>
{
List<string> results = await PerformCPUBoundComputationAsync();
foreach(string result in results)
{
logger.Write($"INFO | Item: {result}");
}
string response = await MakePostRequestAsync(results);
logger.Write($"INFO | Response: {response}");
});
}
catch (Exception ex)
{
logger.Write($"ERROR | {ex.Message}");
}
finally
{
logger.Write($"INFO | Execution Ended");
logger.WriteToFile();
}
}
}

最佳答案

这不是使用您的 Logger 类,我只是想向您展示另一种方法
首先我们不使用Parallel.ForEach对于 IO 绑定(bind)的工作,它是不合适的,我们绝对不会给它一个异步 lambda(这是一个未观察到的 async void ),这意味着 Parallel.ForEach将在所有任务完成之前完成。
至于你的问题:

  • 要解决已完成的任务问题,让我们使用 WhenAll
  • 为了解决线程安全问题,让我们为每个任务制作一个单独的字符串生成器。这是一个小分配,但它是无锁的。
  • 把所有的日志写在最后

  • async 和 await 模式将在完成 IO 绑定(bind)工作时将线程返回到线程池。任务调度程序将使用这些线程进行 CPU 密集型工作。
    var tasks = Directory
    .EnumerateFiles(workingDirectory, "*", SearchOption.AllDirectories)
    .Select(async x =>
    {
    var sb = new StringBuilder();
    List<string> results = // do cpu bound work, no need for fake async,
    // thats to say no need to offload to another thread.
    foreach (string result in results)
    sb.AppendLine($"INFO | Item: {result}");
    string response = await MakePostRequestAsync(results);
    sb.AppendLine($"INFO | Response: {response}");
    return sb;
    });

    // await all your work to finish
    var logs = await Task.WhenAll(tasks);

    // write the results to the file
    using var sw = new StreamWriter("FileName");
    sw.WriteLine($"INFO | Execution Started");

    foreach (var log in logs)
    sw.WriteLine(log);

    sw.WriteLine($"INFO | Execution Ended");
    注意:这可能会导致分配和内存压力,具体取决于日志的大小。在这种情况下,您可能需要返回到同步原语,并承担锁定的代价。
    另一种有效的方法是使用诸如 Tpl Dataflow 管道之类的东西,进行计算,发布帖子,然后将写入的结果批处理,这可能会减少分配,并且具有处理同步和异步工作负载的优势。
    Processing (parallel)
    v
    Posting (parallel)
    v
    Batched Log Writes (Singular)

    关于c# - 共享资源访问的优化同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64477262/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com