How to make C (P/invoke) code called from C# “Thread-safe”

后端 未结 4 705
长情又很酷
长情又很酷 2021-02-08 06:43

I have some simple C-code which uses a single global-variable. Obviously this is not thread-safe, so when I call it from multiple threads in C# using P/invoke, things screw up.

4条回答
  •  傲寒
    傲寒 (楼主)
    2021-02-08 07:03

    Personally if the C code was to be called elsewhere I would use a mutex there. If that doesn't float your boat you can lock in .Net quite easily:

    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);
    

    [Edit..]

    As pointed out, this serializes access to the function which you can't do yourself in this case. You have some C/C++ code that (wrongly IMO) uses a global for state during the call to the exposed function.

    As you have observed that the __declspec(thread) trick doesn't work here then I would try to pass your state/context back and forth as an opaque pointer like 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);
        }
    }
    

    And the corresponding C# wrapper as before:

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

提交回复
热议问题