如何制作C#"线程安全"调用C(P/Invoke)代码[英] How to make C (P/invoke) code called from C# "Thread-safe"

本文是小编为大家收集整理的关于如何制作C#"线程安全"调用C(P/Invoke)代码的处理方法,想解了如何制作C#"线程安全"调用C(P/Invoke)代码的问题怎么解决?如何制作C#"线程安全"调用C(P/Invoke)代码问题的解决办法?那么可以参考本文帮助大家快速定位并解决问题。

问题描述

我有一些简单的C代码,它使用单个全局变量.显然,这不是线程安全,因此,当我使用p/indoke从c#中的多个线程调用它时,事情会拧紧.

如何为每个线程单独导入此功能,或使其使其螺纹安全?

我尝试声明变量__declspec(thread),但这导致程序崩溃.我还尝试制作C ++/CLI类,但是它不允许成员功能为__declspec(naked),我需要(我正在使用内联用户).我并不是很有经验的编写多线程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);
    }
}

[edit] :SomeFunction()的结果必须基于someGlobalVariable 的规定顺序(例如,以someGlobalVariable为someGlobalVariable)作为内部状态).因此,使用静音或其他类型的锁定不是一个选项 - 每个线程必须有自己的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);
}

安全魔术:

[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;
    }
}

其他推荐答案

亲自将C代码在其他地方调用,我将在那里使用Mutex.如果没有漂浮的船,您可以很容易地锁定.NET:

static object SomeFunctionLock = new Object();

public static int SomeFunction(int parameter1, int parameter2){
  lock ( SomeFunctionLock ){
    return _SomeFunction( parameter1, parameter2 );
  }
}

[DllImport("MyDll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int _SomeFunction(int parameter1, int parameter2);

[编辑..]

正如指出的那样,在这种情况下,这序列化了您无法执行的功能的访问.您有一些C/C ++代码(错误的IMO)在调用暴露函数的情况下使用状态全局.

您已经观察到__declspec(thread)技巧在这里不起作用,因此我会尝试以像So这样的不透明指针来回传递您的状态/上下文: -

extern "C" 
{
    int _SomeOtherFunction( void* pctx, int p1, int p2 )
    { 
        return stuff;
    }

    // publically exposed library function
    int __declspec(dllexport) SomeFunction(int parameter1, int parameter2)
    {
        StateContext ctx;
        return _SomeOtherFunction( &ctx, parameter1, parameter2 );
    }

    // another publically exposed library function that takes state
    int __declspec(dllexport) SomeFunctionWithState(StateContext * ctx, int parameter1, int parameter2)
    {
        return _SomeOtherFunction( ctx, parameter1, parameter2 );
    }

    // if you wanted to create/preserve/use the state directly
    StateContext * __declspec(dllexport) GetState(void) {
        ctx = (StateContext*) calloc( 1 , sizeof(StateContext) );
        return ctx;
    }

    // tidy up
    void __declspec(dllexport) FreeState(StateContext * ctx) {
        free (ctx);
    }
}

和相应的C#包装器如前所述:

[DllImport("MyDll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int SomeFunction(int parameter1, int parameter2);

[DllImport("MyDll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int SomeFunctionWithState(IntPtr ctx, int parameter1, int parameter2);

[DllImport("MyDll", CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr GetState();

[DllImport("MyDll", CallingConvention = CallingConvention.Cdecl)]
internal static extern void FreeState(IntPtr);

其他推荐答案

您可以确保您在C#代码中一次一次只称呼_ somefunction,或更改C代码以将对全局变量的访问包装在同步原始性中,例如关键部分.

.

我建议更改C#代码,而不是C代码,因为C#代码是多线程,而不是C代码.

本文地址:https://www.itbaoku.cn/post/359224.html

相关标签/搜索