我有一些使用单个全局变量的简单的C代码.显然这不是线程安全的,所以当我使用P / invoke从C#中的多个线程中调用它时,事情就会变得困难.
如何为每个线程单独导入此函数,或使线程安全?
我尝试声明变量__declspec(线程),但这导致程序崩溃.我也尝试制作一个C/C++LI类,但它不允许成员函数为__declspec(裸),我需要(我正在使用inline-assembly).我编写多线程C代码并不是很有经验,所以可能有一些我错过的东西.
以下是一些示例代码:
C#
[DllImport("MyDll.dll",CallingConvention = CallingConvention.Cdecl)] public static extern int SomeFunction(int parameter1,int parameter2);
C
extern "C" { int someGlobalVariable; int __declspec(naked) _someFunction(int parameter1,int parameter2) { __asm { //someGlobalVariable read/written here } } int __declspec(dllexport) SomeFunction(int parameter1,int parameter2) { return _someFunction(parameter1,parameter2); } }
[编辑]:SomeFunction()的结果必须按照一些规定的顺序,基于someGlobalVariable(例如,一个PRNG,一些GlobalVariable作为内部状态).所以,使用互斥体或其他类型的锁不是一个选项 – 每个线程必须有自己的someGlobalVariable的副本.
解决方法
一个常见的模式是拥有
>为状态分配内存的函数,
>一个没有副作用而是使传入状态变异的函数,
>一个释放状态的备忘录的功能.
C#端看起来像这样:
用法:
var state = new ThreadLocal<SomeSafeHandle>(NativeMethods.CreateSomeState); Parallel.For(0,100,i => { var result = NativeMethods.SomeFunction(state.Value,i,42); Console.WriteLine(result); });
声明:
internal static class NativeMethods { [DllImport("MyDll.dll",CallingConvention = CallingConvention.Cdecl)] public static extern SomeSafeHandle CreateSomeState(); [DllImport("MyDll.dll",CallingConvention = CallingConvention.Cdecl)] public static extern int SomeFunction(SomeSafeHandle handle,int parameter1,int parameter2); [DllImport("MyDll.dll",CallingConvention = CallingConvention.Cdecl)] internal static extern int FreeSomeState(IntPtr handle); }
SafeHandle魔术:
[SecurityPermission(SecurityAction.InheritanceDemand,UnmanagedCode = true)] [SecurityPermission(SecurityAction.Demand,UnmanagedCode = true)] internal class SomeSafeHandle : SafeHandle { [ReliabilityContract(Consistency.WillNotCorruptState,Cer.MayFail)] public SomeSafeHandle() : base(IntPtr.Zero,true) { } public override bool IsInvalid { get { return this.handle == IntPtr.Zero; } } [ReliabilityContract(Consistency.WillNotCorruptState,Cer.MayFail)] protected override bool ReleaseHandle() { return NativeMethods.FreeSomeState(this.handle) == 0; } }