我已将结构数组从C#(托管代码)传递到C(非托管代码)。 struct的内存分配在C#端。该数组用C代码填充。我的代码是多线程的。数组的填充由一个线程完成,另一个线程从struct数组中读取已填充的项目。但是我无法从第二个线程读取数据,直到第一个线程退出,但是两者共享内存。
示例代码
C#结构
public struct Data
{
public IntPtr str;
[MarshalAs(UnmanagedType.I4)]
public int id;
}
在C#端分配结构数组的内存
GCHandle[] handles = new GCHandle[10];
for (int i = 0; i < 10; i++)
{
_data[i] = new Data();
byte[] bd = new byte[100];
handles[i] = GCHandle.Alloc(bd, GCHandleType.Pinned);
data[i].str = handles[i].AddrOfPinnedObject();
}
第一个线程将此结构数组_data传递给非托管代码(C代码)以使用func填充
void func([In,Out] Data[] _data);
第二个线程开始读取已填充的结构数据,但第一个线程仍在填充数据以进行剩余索引。在这种情况下,数据可用于
_data[0].str
但
_data[0].id
显示0,但在C端显示正确的填充值,即5.请帮助为什么struct成员int id的数据将变为0。正确的值仅在第一个线程结束之后出现。但是,由于内存是共享的,因此必须在非托管代码填充后立即可用。
我们将不胜感激任何帮助。
即使线程1没有结束,我也希望线程2必须能够获取已经在非托管代码中填充的索引值。
答案 0 :(得分:2)
因为将托管数组传递给非托管代码,clr会先将数组复制到另一个内存块中,然后再将其传递给非托管函数。非托管函数完成后,clr将把内存封送回托管数组,然后得到结果。
一种解决方案是直接将数组地址与不安全的代码一起使用。
extern void func(Data* data);
fixed(Data* p = _data)
{
func(p);
}
如果您不喜欢不安全的代码,由于结构数组的存储是顺序的,因此可以传递第一个元素的引用。但是我不确定这是否足够安全。
extern void func(ref Data data);
func(ref _data[0]);