我正在用 c# 编写一个软件,它需要多次调用多个线程并在 c++ 非托管 dll 中调用一个函数。
我有一个这样的 C++ 文件:
// "variables" which consist in some simple variables (int, double)
// and in some complex variables (structs containing arrays of structs)
extern "C"
{
__declspec(dllexport) int function1()
{
// some work depending on random and on the "variables"
}
}
还有一个类似的 C# 类
public class class1
{
// "variables" <--- the "same" as the C++ file's ones
// Dll import <--- ok
public void method1()
{
int [] result;
for(int i=0; i<many_times; i++)
{
result = new int[number_of_parallel_tasks];
Parallel.For(0, number_of_parallel_tasks, delegate(int j)
{
// I would like to do result[j] = function1()
});
// choose best result
// then update "variables"
}
}
}
我写了“我想做...”,因为 c++ 函数也需要在每一轮更新“变量”。
我的问题是:
是否可以在 C++ 和 C# 之间共享内存以避免每次都传递引用?只是浪费时间吗?
我阅读了有关内存映射文件的信息。他们能帮助我吗?但是,您知道更合适的解决方案吗?
非常感谢。
最佳答案
一旦你知道它是如何工作的,使用 P/Invoke 在 C# 和 C++ 之间共享内存就没有问题了。我建议阅读 MSDN 中的编码。您可能还想了解如何使用 unsafe 关键字和修复内存。
这是一个假设您的变量可以描述为简单结构的示例:
在 C++ 中声明你的函数如下:
#pragma pack(1)
typedef struct VARIABLES
{
/*
Use simple variables, avoid pointers
If you need to use arrays use fixed size ones
*/
}variables_t;
#pragma pack()
extern "C"
{
__declspec(dllexport) int function1(void * variables)
{
// some work depending on random and on the "variables"
}
}
在 C# 中做这样的事情:
[StructLayout(LayoutKind.Sequential, Pack=1)]
struct variables_t
{
/*
Place the exact same definition as in C++
remember that long long in c++ is long in c#
use MarshalAs for fixed size arrays
*/
};
[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern int function(ref variables_t variables);
在你的类里面:
variables_t variables = new variables_t();
//Initialize variables here
for(int i=0; i<many_times; i++)
{
int[] result = new int[number_of_parallel_tasks];
Parallel.For(0, number_of_parallel_tasks, delegate(int j)
{
result[j] = function1(ref variables)
});
// choose best result
// then update "variables"
}
您可以使用更复杂的场景,例如在 c++ 中分配和释放结构,使用其他形式的编码(marshal)处理来获取数据,例如构建您自己的类以直接读取和写入非托管内存。但是如果你可以使用一个简单的结构来保存你的变量,上面的方法是最简单的。
编辑:关于如何正确处理更复杂数据的指针
所以我认为上面的示例是在 C# 和 C++ 之间“共享”数据的正确方法,如果它是简单数据,例如。保存原始类型或原始类型固定大小的数组的结构。
这就是说,使用 C# 访问内存的方式实际上几乎没有限制。有关更多信息,请查看 unsafe 关键字、fixed 关键字和 GCHandle 结构。而且,如果您有一个非常复杂的数据结构,其中包含其他结构的数组等,那么您的工作就会更加复杂。
在上述情况下,我建议将有关如何将“变量”更新为 C++ 的逻辑。 在 C++ 中添加一个函数,如下所示:
extern "C"
{
__declspec(dllexport) void updateVariables(int bestResult)
{
// update the variables
}
}
我仍然建议不要使用全局变量,所以我提出以下方案。 在 C++ 中:
typedef struct MYVERYCOMPLEXDATA
{
/*
Some very complex data structure
*/
}variables_t;
extern "C"
{
__declspec(dllexport) variables_t * AllocVariables()
{
// Alloc the variables;
}
__declspec(dllexport) void ReleaseVariables(variables_t * variables)
{
// Free the variables;
}
__declspec(dllexport) int function1(variables_t const * variables)
{
// Do some work depending on variables;
}
__declspec(dllexport) void updateVariables(variables_t * variables, int bestResult)
{
// update the variables
}
};
在 C# 中:
[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern IntPtr AllocVariables();
[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern void ReleaseVariables(IntPtr variables);
[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern int function1(IntPtr variables);
[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern void updateVariables(IntPtr variables, int bestResult);
如果您仍想在 C# 中维护您的逻辑,则必须执行以下操作: 创建一个类来保存从 C++ 返回的内存并编写您自己的内存访问逻辑。使用复制语义将数据公开给 C#。我的意思如下, 假设您在 C++ 中有这样的结构:
#pragma pack(1)
typedef struct SUBSTRUCT
{
int subInt;
double subDouble;
}subvar_t;
typedef struct COMPLEXDATA
{
int int0;
double double0;
int subdata_length;
subvar_t * subdata;
}variables_t;
#pragma pack()
在 C# 中你可以做这样的事情
[DllImport("kernel32.dll")]
static extern void CopyMemory(IntPtr dst, IntPtr src, uint size);
[StructLayout((LayoutKind.Sequential, Pack=1)]
struct variable_t
{
public int int0;
public double double0;
public int subdata_length;
private IntPtr subdata;
public SubData[] subdata
{
get
{
SubData[] ret = new SubData[subdata_length];
GCHandle gcH = GCHandle.Alloc(ret, GCHandleType.Pinned);
CopyMemory(gcH.AddrOfPinnedObject(), subdata, (uint)Marshal.SizeOf(typeof(SubData))*subdata_length);
gcH.Free();
return ret;
}
set
{
if(value == null || value.Length == 0)
{
subdata_length = 0;
subdata = IntPtr.Zero;
}else
{
GCHandle gcH = GCHandle.Alloc(value, GCHandleType.Pinned);
subdata_length = value.Length;
if(subdata != IntPtr.Zero)
Marshal.FreeHGlobal(subdata);
subdata = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SubData))*subdata_length);
CopyMemory(subdata, gcH.AddrOfPinnedObject(),(uint)Marshal.SizeOf(typeof(SubData))*subdata_length);
gcH.Free();
}
}
}
};
[StructLayout((LayoutKind.Sequential, Pack=1)]
sturct SubData
{
public int subInt;
public double subDouble;
};
在上面的示例中,仍然可以像在第一个示例中一样传递结构。这当然只是关于如何使用结构数组和结构数组中的结构数组处理复杂数据的概述。如您所见,您将需要大量复制来保护自己免受内存损坏。此外,如果内存是通过 C++ 分配的,那么使用 FreeHGlobal 释放它会非常糟糕。 如果您想避免复制内存并仍然维护 C# 中的逻辑,您可以编写一个带有访问器的 native 内存包装器,用于您想要的任何内容。例如,您将有一个方法来直接设置或获取第 N 个数组成员的 subInt - 这种方式您将保留您的拷贝,与您访问的内容完全相同。
另一种选择是编写特定的 C++ 函数来为您处理困难的数据,并根据您的逻辑从 C# 中调用它们。
最后但并非最不重要的一点是,您始终可以将 C++ 与 CLI 接口(interface)一起使用。但是,我自己只有在必须时才这样做 - 我不喜欢术语,但对于非常复杂的数据,您当然必须考虑它。
编辑
为了完整起见,我向 DllImport 添加了正确的调用约定。请注意,DllImport 属性使用的默认调用约定是 Winapi(在 Windows 上转换为 __stdcall),而 C/C++ 中的默认调用约定(除非您更改编译器选项)是 __cdecl。
关于c# - 在 C# 和 C++ 之间共享变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13275264/
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击
我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
在Cooper的书BeginningRuby中,第166页有一个我无法重现的示例。classSongincludeComparableattr_accessor:lengthdef(other)@lengthother.lengthenddefinitialize(song_name,length)@song_name=song_name@length=lengthendenda=Song.new('Rockaroundtheclock',143)b=Song.new('BohemianRhapsody',544)c=Song.new('MinuteWaltz',60)a.betwee
我正在检查一个Rails项目。在ERubyHTML模板页面上,我看到了这样几行:我不明白为什么不这样写:在这种情况下,||=和ifnil?有什么区别? 最佳答案 在这种特殊情况下没有区别,但可能是出于习惯。每当我看到nil?被使用时,它几乎总是使用不当。在Ruby中,很少有东西在逻辑上是假的,只有文字false和nil是。这意味着像if(!x.nil?)这样的代码几乎总是更好地表示为if(x)除非期望x可能是文字false。我会将其切换为||=false,因为它具有相同的结果,但这在很大程度上取决于偏好。唯一的缺点是赋值会在每次运行
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
我收到格式为的回复#我需要将其转换为哈希值(针对活跃商家)。目前我正在遍历变量并执行此操作:response.instance_variables.eachdo|r|my_hash.merge!(r.to_s.delete("@").intern=>response.instance_eval(r.to_s.delete("@")))end这有效,它将生成{:first="charlie",:last=>"kelly"},但它似乎有点hacky和不稳定。有更好的方法吗?编辑:我刚刚意识到我可以使用instance_variable_get作为该等式的第二部分,但这仍然是主要问题。