2012-04-30 9 views
10

मेरे पास कुछ सरल सी-कोड है जो एकल वैश्विक चर का उपयोग करता है। जाहिर है यह थ्रेड-सुरक्षित नहीं है, इसलिए जब मैं इसे पी/इनवॉक का उपयोग करके सी # में एकाधिक थ्रेड से कॉल करता हूं, तो चीजें खराब हो जाती हैं।सी # (थ्रेड-सुरक्षित "से बुलाया गया सी (पी/इनवोक) कोड कैसे बनाएं

मैं या तो प्रत्येक थ्रेड के लिए अलग-अलग फ़ंक्शन कैसे आयात कर सकता हूं, या इसे थ्रेड-सुरक्षित बना सकता हूं?

मैंने परिवर्तनीय __declspec(thread) घोषित करने का प्रयास किया, लेकिन इससे प्रोग्राम क्रैश हो गया। मैंने सी ++/सीएलआई कक्षा बनाने की भी कोशिश की, लेकिन यह सदस्य-कार्यों को __declspec(naked) होने की अनुमति नहीं देता है, जिसे मुझे (मैं इनलाइन-असेंबली का उपयोग कर रहा हूं) की आवश्यकता है। मैं बहुत अनुभवी लेखन बहु-थ्रेडेड सी ++ कोड नहीं हूं, इसलिए कुछ ऐसा हो सकता है जो मुझे याद आ रही है।

सी #

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

सी ++

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

[संपादित करें]:का परिणाम


यहाँ कुछ उदाहरण कोड है 10 someGlobalVariable(उदाहरण के बारे में सोचें) के आधार पर कुछ निर्धारित आदेश में जाना चाहिए। एक पीआरएनजी, someGlobalVariable आंतरिक स्थिति के रूप में)। तो, एक म्यूटेक्स या अन्य प्रकार के लॉक का उपयोग करना एक विकल्प नहीं है - प्रत्येक थ्रेड की अपनी प्रति someGlobalVariable की प्रतिलिपि होनी चाहिए।

+0

आप इसे कितनी बार कहते हैं - क्या आप सी # पक्ष में लॉकिंग कर सकते हैं, मानते हैं कि आपके पास सी नहीं है कोड? –

+0

@ स्टेवटाउनसेंड: नहीं, अधिकांशतः सभी सारे थ्रेड उस कार्य को कॉल करते हैं। –

+3

क्या यह एक वैश्विक चर होना है? आप राज्य को फ़ंक्शन से वापस कर सकते हैं और किसी राज्य को अगली बार कॉल करने पर फ़ंक्शन पर वापस जाने की आवश्यकता होती है। (या राज्य के लिए एक सूचक।) – dtb

उत्तर

7

एक आम पैटर्न

  • एक समारोह है कि राज्य के लिए स्मृति आबंटित करता है,
  • एक समारोह है कि कोई दुष्प्रभाव लेकिन है पारित कर दिया-इन राज्य, और
  • एक समारोह परिवर्तनशील जो राज्य के लिए ज्ञापन जारी करता है।

सी # ओर इस प्रकार दिखाई देगा:

उपयोग:

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

व्यक्तिगत रूप से अगर सी कोड को कहीं और कहा जाना था तो मैं वहां एक म्यूटक्स का उपयोग करूंगा। कि अपनी नाव नाव नहीं है, तो आप काफी आसानी से नेट में लॉक कर सकते हैं:

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

[संपादित करें ..]

के रूप में कहे अनुसार, इस समारोह के लिए उपयोग को धारावाहिक जो आप नहीं कर सकते इस मामले में खुद को करो। आपके पास कुछ सी/सी ++ कोड है जो (गलत तरीके से आईएमओ) खुलासा समारोह में कॉल के दौरान राज्य के लिए वैश्विक का उपयोग करता है।

आप देखा है कि __declspec(thread) चाल यहाँ काम नहीं करता तो मैं बहुत की तरह एक अपारदर्शी सूचक के रूप में आगे और पीछे अपने राज्य/संदर्भ पारित करने के लिए कोशिश करेंगे के रूप में: -

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

और इसी सी # आवरण के रूप में पहले:

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

यह अनिवार्य रूप से मेरे धागे को क्रमबद्ध करेगा। ग्लोबल-वेरिएबल को थ्रेड-लोकल स्टोरेज में संग्रहीत करने का कोई तरीका नहीं है? –

+0

आपके अप्रबंधित कोड में एक वैश्विक है? वैसे तो एक म्यूटेक्स बनाओ। – IanNorton

+0

सी कोड बदलने के बिना? नहीं। भले ही प्रत्येक सी # थ्रेड स्थानीय प्रतिलिपि को स्टोर कर सके, फिर भी सी 'मास्टर कॉपी' को संशोधित करने वाले अन्य धागे बनाम फ़ायरवॉल कैसे नहीं है। –

2

तुम्हें यकीन है कि तुम क्या केवल _someFunction एक बार एक समय में अपने सी # कोड में कॉल या एक तुल्यकालन एक महत्वपूर्ण खंड की तरह आदिम में वैश्विक चर के लिए उपयोग रैप करने के लिए सी कोड परिवर्तन कर सकते हैं या तो।

मैं सी कोड के बजाय सी # कोड बदलने की सिफारिश करता हूं, क्योंकि सी # कोड बहु-थ्रेडेड है, सी कोड नहीं।

1

अच्छी खबर यह है, तो आप एक __declspec(naked) समारोह सी के एक सदस्य के रूप में बना सकते हैं ++ (गैर CLI) वर्ग:

class A { 
    int n; 
public: 
    A() { n = 0; } 
    void f(int n1, int n2); 
}; 

__declspec(naked) void A::f(int n1, int n2) 
{ 
    n++; 
} 

बुरी खबर, आप कॉम की आवश्यकता होगी करने के लिए सक्षम होने के लिए इस तरह के वर्ग का उपयोग करें। यह सही है: सी ++ में लिपटे एएसएम, आरसीडब्लू में लिपटे, सीएलआर में लिपटे ...