gpt4 book ai didi

c# - 如何在 Rust 中并行创建一个数组并将其返回给 C#?

转载 作者:太空狗 更新时间:2023-10-30 01:12:36 25 4
gpt4 key购买 nike

我尝试在 Rust 中并行创建一个数组,并通过 DLL 绑定(bind)将其返回给 C#。前 4 个元素无效。

没有 ThreadPool 和同步,我得到了正确的结果。真正的计算比较复杂,但是下面的代码是没有真正计算的简化版。我也尝试过 int* 而不是 IntPtr 但得到了相同的无效结果。

最后,我是 Rust 的新手,欢迎任何改进代码的建议。

简化的 Rust 计算

#[no_mangle]
pub extern "C" fn create_array(len: libc::c_int, result:*mut *mut libc::c_int){
let mut result_vec: Vec<libc::c_int> = vec![0;len as usize];
let sync_result=Arc::new(Mutex::new(result_vec));
let pool=ThreadPool::new(6);

println!("From Thread");
for i in 0..(len){
pool.execute({
let clone = Arc::clone(&sync_result);
move||{
let mut result_vec = clone.lock().unwrap();
result_vec[i as usize]=i;
if i<10{
println!("{}:{}",i,result_vec[i as usize]);
}
}});
}
pool.join();

let mut result_vec = Arc::try_unwrap(sync_result).unwrap().into_inner().unwrap();

println!("Unwrapped Vector");
for i in 0..10{
println!("{}:{}",i,result_vec[i as usize]);
}
let result_data = result_vec.as_mut_ptr();
unsafe{
println!("Raw data");
*result=result_data;
for i in 0..10 as isize{
println!("{}:{}",i,ptr::read(result_data.offset(i)));
}
}
std::mem::forget(result_data);
}

C#绑定(bind)和函数调用

[DllImport(@"libs\OptimizationRust.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void create_array(int len, out IntPtr result);
public void RustCpuSerial()
{
IntPtr resultPtr;
int len = 10000;
create_array(len,out resultPtr);

int[] results = new int[len];
Marshal.Copy(resultPtr, results, 0, results.Length);
}

Rust 输出:

From Thread
0:0
5:5
7:7
8:8
9:9
1:1
3:3
4:4
6:6
2:2

Unwrapped Vector
0:0
1:1
2:2
3:3
4:4
5:5
6:6
7:7
8:8
9:9

Raw data
0:0
1:1
2:2
3:3
4:4
5:5
6:6
7:7
8:8
9:9

C# 输出:

0:-314008176
1:672
2:-314139296
3:672
4:4
5:5
6:6
7:7
8:8
9:9

任何想法,是什么导致了这种行为?

最佳答案

首先,抱歉,FFI 障碍的后半部分将是 C。我没有合适的环境来展示我的 C# 技能。

让我们先稍微总结一下您的代码,然后放弃所有线程(因为它什么都不做,只是在问题不存在时让我们感到困惑)

使用rust 的一半实际上是这样的:

#[no_mangle]
pub extern "C" fn create_array(len: libc::c_int, result:*mut *mut libc::c_int){ // You're passing a pointer to a collection
let mut result_vec: Vec<i32> = vec![]; // You create a Vec<>
for i in 0..(len){
result_vec.push(i); // You fill it...
}
let result_data = result_vec.as_mut_ptr(); // You then get a *mut ptr to it
unsafe{
*result=result_data; // You then assign the content of the pointer-to-a-pointer of what you received to the result ptr you just acquired
}
std::mem::forget(result_data); // And then you forget your data, because it is referenced elsewhere
}

这是在任何更改之前。我添加了评论以总结您最终所做的事情。

果然,当从 C 执行 FFI 时,我可以使用这段代码重现错误:

enter image description here

这是“固定”版本:

pub extern "C" fn create_array(len: libc::c_int,result:*mut *mut libc::c_int){
let mut results_vec: Vec<i32> = vec![];
for i in 0..(len) {
results_vec.push(i);
}

let mut result_slice = results_vec.into_boxed_slice(); // Change #1: Vec -> Box<[i32]>
let result_data = result_slice.as_mut_ptr(); // We then get a *mut out of it
unsafe {
*result = result_data; // Same step as yours, we then overwrite the old pointer with its address
// Do note that you may have leaked memory there if the pointer is not null.
for i in 0..10 as isize{
println!("{}:{}",i,ptr::read((*result).offset(i)));
}
}
// We then forget *the boxed slice*. This is the important bit.
std::mem::forget(result_slice);
}

这行得通,至少有少量用 C 编写的测试代码。它行得通而原始版本不行的原因是你没有忘记正确的事情,主要是 - 你忘记了 指针Vec,而不是 Vec 本身。结果,整个内存块在技术上未被初始化,并且显然在 FFI 使用和您打印出数据之间用于其他事情。

在实践中,您将调用 Box::into_raw(result_slice),而不是调用 result_slice.as_mut_ptr(),它的优点是不会让您必须记住忘记切片。

关于c# - 如何在 Rust 中并行创建一个数组并将其返回给 C#?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57773532/

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