rust 保持输入到输出的顺序,而每个输出来自一个应该读取一次的文件

5cnsuln7  于 2023-11-19  发布在  其他
关注(0)|答案(1)|浏览(131)

我正在努力想出最好的方法来构建我的程序,关于一个特定的功能。

以下是一些细节:

  • 函数的输入是一个(纬度,经度)坐标向量
  • 这些可以是 * 无论用户提供的顺序 *
  • 函数的输出应为输入坐标处的高程,顺序与输入相同
  • 通过访问TIFF文件中的像素值来检索高程,TIFF文件由整数纬度,经度坐标命名,例如n40w106n40w107,.。
  • 此数字表示左上角,例如:
  • (39.5,-105.5),(39.8,-105.2)都是n40w106.tif
  • (40.3,-106.1),(40.5,-106.9)都是n41w107.tif
  • 这意味着可能需要从同一文件中检索输入向量中不同点的多个点
  • 我已经有了所有的功能,以采取输入点和检索海拔从相应的文件

我正在努力解决的是我想确保的这些额外要求:

  • 每个文件应该只加载一次
  • 一次只能有一个文件在内存中(所以我不认为缓存会起作用)
  • 输出的顺序应与提供的输入的顺序相对应
    CS中是否有一些现有的范例或概念可以实现我想要的?

我还想知道是否有一种模型可以很好地处理并发。作为参考,我使用Rust编程语言。

mwngjboj

mwngjboj1#

每个文件只应该被加载一次,并且它们只应该在使用时保存在内存中,这意味着你必须提前确定一组都来自同一个文件的组,否则你不能同时满足这两个约束。
为了保持顺序,可以提前创建输出缓冲区,然后,当将输入列表组织成组时,可以将其索引包含在输入列表中,以便处理该项的代码可以将其直接写入输出缓冲区中的正确槽中。
最后,您将每个组发送到一个线程,在该线程中可以一次性处理单个文件的所有条目。
请注意,由于Rust的别名规则,线程通常不能在没有某种互斥保护的情况下同时写入缓冲区,即使它们写入不同的索引。但是,您可以通过在输出缓冲区中使用split_first_mut来解决这个问题,将可变引用存储到应该存储输出值的位置。这样就没有别名,* 和 * 不需要互斥。
您还需要使用scoped threads,以便将这些可变切片发送到线程。
最后,返回输出缓冲区。
下面是一个函数的例子,它接受一个数字对(a, b)的列表,其中处理一个数字对的结果是它们的乘积,但是具有相同b值的所有数字对应该一起处理:

  1. use std::collections::HashMap;
  2. fn process_groups(items: &[(i32, i32)]) -> Vec<i32> {
  3. let mut output = vec![0; items.len()];
  4. let mut output_head = output.as_mut_slice();
  5. let mut groups: HashMap<i32, Vec<(i32, &mut i32)>> = HashMap::new();
  6. for &(a, b) in items {
  7. let (output_pos, tail) = output_head.split_first_mut().unwrap();
  8. output_head = tail;
  9. groups.entry(b).or_default().push((a, output_pos));
  10. }
  11. std::thread::scope(|s| {
  12. for (b, items) in groups {
  13. s.spawn(move || {
  14. for (a, output) in items {
  15. *output = a * b;
  16. }
  17. });
  18. }
  19. });
  20. output
  21. }
  22. #[test]
  23. fn example() {
  24. assert_eq!(
  25. process_groups(&[(2, 4), (16, 3), (7, 4), (9, 4), (25, 2), (18, 3),]),
  26. vec![2 * 4, 16 * 3, 7 * 4, 9 * 4, 25 * 2, 18 * 3]
  27. );
  28. }

字符串
Playground
这里有几个需要注意的警告:

  • 由于你的代码需要从文件中读取,所以在你的例子中处理函数是容易出错的。你需要一种方法来将错误传递回调用者。你可以让任何错误导致整个操作失败(返回Result<Vec<_>, _>),或者你可以通过允许个别结果失败来返回尽可能多的结果(返回Vec<Result<_, _>>)。
  • 示例代码假设您可以构造一个默认值来初始填充输出缓冲区。如果不存在这样的值,您可能需要使用None作为占位符,并在线程中使用Some值填充它。或者,有一个涉及不安全代码和MaybeUninit的过程,在该过程中,您可以为项分配所需的空间,而无需使用Option * 或 * 提前初始化它们,但是这样做的代码很难正确,并且代码中的任何错误都可能导致可靠性问题。
  • 示例代码为每个组创建一个线程。创建固定数量的线程并使用channel将作业排队到这些线程可能会更有效。
展开查看全部

相关问题