如何从C#调用C(P/invoke)代码"线程安全"

Blu*_*eft 10 c c# pinvoke multithreading

我有一些使用单个全局变量的简单C代码.显然这不是线程安全的,所以当我使用P/invoke从C#中的多个线程调用它时,事情搞砸了.

如何为每个线程单独导入此函数,还是使其线程安全?

我尝试声明变量__declspec(thread),但这导致程序崩溃.我也试过制作C++/CLI类,但它不允许成员函数__declspec(naked),我需要(我使用的是内联汇编).我编写多线程C++代码并不是很有经验,所以我可能会遗漏一些东西.


这是一些示例代码:

C#

[DllImport("MyDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int SomeFunction(int parameter1, int parameter2);
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

[编辑]:结果SomeFunction()必须以某种规定的顺序为基础someGlobalVariable (例如,考虑一个PRNG,someGlobalVariable作为内部状态).因此,使用互斥锁或其他类型的锁不是一个选项 - 每个线程必须有自己的副本someGlobalVariable.

dtb*_*dtb 7

一种常见的模式是

  • 一个为状态分配内存的函数,
  • 一个没有副作用但改变传入状态的函数,和
  • 释放状态的memoy的函数.

C#端看起来像这样:

用法:

var state = new ThreadLocal<SomeSafeHandle>(NativeMethods.CreateSomeState);

Parallel.For(0, 100, i =>
{
    var result = NativeMethods.SomeFunction(state.Value, i, 42);

    Console.WriteLine(result);
});
Run Code Online (Sandbox Code Playgroud)

声明:

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);
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)