人造丝为什么不要求Arc <_>?

时间:2019-12-23 01:59:41

标签: rust automatic-ref-counting lifetime-scoping

Programming Rust的第465页上,您可以找到代码和说明(我加了强调)

use std::sync::Arc;

fn process_files_in_parallel(filenames: Vec<String>,
                             glossary: Arc<GigabyteMap>)
    -> io::Result<()>
{
    ...
    for worklist in worklists {
        // This call to .clone() only clones the Arc and bumps the
        // reference count. It does not clone the GigabyteMap.
        let glossary_for_child = glossary.clone();
        thread_handles.push(
            spawn(move || process_files(worklist, &glossary_for_child))
        );
    }
    ...
}
     

我们更改了词汇表的类型:要并行运行分析,调用者必须传递Arc<GigabyteMap>(指向已移入堆中的GigabyteMap的智能指针),方法是执行{ {1}}。当我们调用lossary.clone()时,我们正在复制Arc::new(giga_map)智能指针,而不是整个Arc。这相当于增加参考计数。进行此更改后,程序将编译并运行,因为它不再依赖于参考生存期。 只要任何线程拥有GigabyteMap,即使父线程提早解救,它也将使映射保持活动状态。不会有任何数据争用,因为Arc<GigabyteMap>中的数据是不可变的。

在下一部分中,他们显示了用人造丝重写的内容,

Arc

您可以在改写为使用Rayon的部分中看到,它接受extern crate rayon; use rayon::prelude::*; fn process_files_in_parallel(filenames: Vec<String>, glossary: &GigabyteMap) -> io::Result<()> { filenames.par_iter() .map(|filename| process_file(filename, glossary)) .reduce_with(|r1, r2| { if r1.is_err() { r1 } else { r2 } }) .unwrap_or(Ok(())) } 而不是&GigabyteMap。他们没有解释这是如何工作的。人造丝为什么不要求Arc<GigabyteMap>?人造丝如何摆脱直接引用?

1 个答案:

答案 0 :(得分:2)

与第一个代码示例中假设的indirect enum Items: Codable { /** Initialises an `Items` instance by decoding from the given `decoder`. - Parameter decoder: The decoder to read data from. */ init(from decoder: Decoder) throws { var itemsSet: Set<Items> = try decodeItems(from: decoder) if itemsSet.count == 1 { self = ItemsSet.popFirst()! } else { self = .allOfItems(itemsSet) } } /** Encodes an `Items` instance`. - Parameter encoder: The encoder to encode data to. */ func encode(to encoder: Encoder) throws { var container = encoder.unkeyedContainer() switch self { case .item(let item): try container.encode(item) case .allOfItems(let items): try container.encode(contentsOf: items) case .anyOfItems(let items): try container.encode(AnyOfItems(Items.anyOfItems(items))) } } case item(NameVersion) case anyOfItems(Set<Items>) case allOfItems(Set<Items>) } struct AnyOfItems: Codable { /** Initialises an `Items` instance by decoding from the given `decoder`. - Parameter decoder: The decoder to read data from. */ init(from decoder: Decoder) throws { var itemsSet: Set<Items> = try decodeItems(from: decoder) if itemsSet.count == 1 { items = itemsSet.popFirst()! } else { items = Items.anyOfItems(itemsSet) } } /** Encodes an `Items` instance`. - Parameter encoder: The encoder to encode data to. */ func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(items, forKey: .items) } /** A memberwise initialiser. */ init(_ items: Items) { self.items = items } let items: Items private enum CodingKeys: String, CodingKey { case items = "any_of" } } func decodeItems(from decoder: Decoder) throws -> Set<Items> { var itemsSet: Set<Items> = [] var unkeyedValues = try decoder.unkeyedContainer() while unkeyedValues.count! > unkeyedValues.currentIndex { let containerIndexBeforeLoop = unkeyedValues.currentIndex if let nameVersion = try? unkeyedValues.decode(NameVersion.self) { itemsSet.insert(Items.item(nameVersion)) } else if let anyOfItems = try? unkeyedValues.decode(AnyOfItems.self) { itemsSet.insert(anyOfItems.items) } else if let items = try? unkeyedValues.decode(Items.self) { itemsSet.insert(items) } if unkeyedValues.currentIndex <= containerIndexBeforeLoop { break } } return itemsSet } 不同,Rayon可以保证迭代器不会超过当前的堆栈帧。具体来说,引擎盖下的thread::spawn使用类似于Rayon的scope函数的功能,该函数允许生成一个“附加”到堆栈上的工作单元,并在堆栈结束之前加入。

由于Rayon可以(从用户的角度出发,保证通过生命周期限制)任务/线程在调用par_iter的函数退出之前已加入,因此它可以提供比标准库更符合人体工程学的API。 par_iter

人造丝在scope function's documentation中对此进行了扩展。