2012-04-30 30 views
10

Tôi có một số mã C đơn giản sử dụng một biến toàn cầu đơn. Rõ ràng đây không phải là thread-an toàn, vì vậy khi tôi gọi nó từ nhiều chủ đề trong C# bằng cách sử dụng P/gọi, mọi thứ vít lên.Cách tạo mã C (P/gọi) được gọi từ C# "An toàn chủ đề"

Làm cách nào để nhập chức năng này một cách riêng biệt cho từng chuỗi hoặc làm cho chuỗi này an toàn?

Tôi đã cố gắng khai báo biến số __declspec(thread), nhưng điều đó khiến chương trình gặp sự cố. Tôi cũng đã thử tạo một lớp C++/CLI, nhưng nó không cho phép các hàm thành viên là __declspec(naked), mà tôi cần (Tôi đang sử dụng lắp ráp nội tuyến). Tôi không có kinh nghiệm viết mã C++ đa luồng, vì vậy có thể có một cái gì đó tôi đang thiếu.


Dưới đây là một số mã ví dụ:

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

[Chỉnh sửa]: Kết quả của SomeFunction() phải theo một số thứ tự được quy định dựa trên someGlobalVariable(ví dụ: PRNG, với someGlobalVariable làm trạng thái nội bộ). Do đó, việc sử dụng mutex hoặc loại khóa khác không phải là tùy chọn - mỗi chủ đề phải có bản sao riêng là someGlobalVariable.

+0

Bạn có thường xuyên gọi điều này - bạn có thể thực hiện khóa ở mặt C#, giả sử bạn không sở hữu C mã? –

+0

@SteveTownsend: Không, chủ yếu là tất cả các chủ đề đa làm là gọi hàm đó. –

+3

Liệu nó có phải là một biến toàn cục? Bạn có thể trả về trạng thái từ hàm và yêu cầu một trạng thái được truyền lại cho hàm khi nó được gọi là lần sau. (Hoặc một con trỏ đến trạng thái.) – dtb

Trả lời

7

Một mô hình phổ biến là có

  • một hàm phân bổ bộ nhớ cho nhà nước,
  • một chức năng mà không có tác dụng phụ nhưng đột biến các thông qua trong tiểu bang, và
  • một chức năng phát hành các memoy cho nhà nước.

C# bên sẽ trông như thế này:

Cách sử dụng:

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

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

    Console.WriteLine(result); 
}); 

khai báo:

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

diệu 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

Cá nhân nếu mã C được gọi ở nơi khác, tôi sẽ sử dụng một mutex ở đó. Nếu điều đó không có nổi chiếc thuyền của bạn, bạn có thể khóa trong Net khá dễ dàng:

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

[Chỉnh sửa ..]

Như đã chỉ ra, điều này serializes truy cập vào các chức năng mà bạn không thể tự làm trong trường hợp này. Bạn có một số mã C/C++ (IMO sai) sử dụng toàn cầu cho trạng thái trong khi gọi hàm tiếp xúc.

Như các bạn đã quan sát thấy rằng các trick __declspec(thread) không làm việc ở đây thì tôi sẽ cố gắng để vượt qua trạng thái của bạn/bối cảnh qua lại như một con trỏ đục như vậy: -

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

Và tương ứng với C# wrapper như trước đây:

[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

Điều này về cơ bản sẽ serialize chủ đề của tôi. Không có cách nào để biến biến toàn cục được lưu trữ trong bộ nhớ cục bộ thay thế? –

+0

Mã không được quản lý của bạn có một mã toàn cầu? Vâng làm một mutex sau đó. – IanNorton

+0

Nếu không thay đổi mã C? Không. Ngay cả khi mỗi chủ đề C# có thể lưu trữ một bản sao cục bộ một số cách vẫn không có tường lửa và các chủ đề khác sửa đổi bản sao chủ 'C'. –

2

bạn có thể chắc chắn rằng những gì bạn chỉ gọi _someFunction một lần tại một thời điểm trong mã C# của bạn hoặc thay đổi mã C để bọc các quyền truy cập vào các biến toàn cầu trong một đồng bộ hóa ban như một phần quan trọng.

Tôi khuyên bạn nên thay đổi mã C# thay vì mã C, vì mã C# là đa luồng, chứ không phải mã C.

1

Tin tốt lành, bạn thể tạo một hàm __declspec(naked) như một thành viên của C++ (không CLI) lớp:

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

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

Tin xấu, bạn sẽ cần COM để có thể sử dụng lớp đó. Đó là đúng: asm bọc trong C + +, bọc trong COM, bọc trong RCW, bọc trong CLR ...

Các vấn đề liên quan