作者热门文章
- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我尝试在 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 时,我可以使用这段代码重现错误:
这是“固定”版本:
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/
我是一名优秀的程序员,十分优秀!