CRITICAL_SECTION in c#

2019-08-22 09:38发布

All. I need to use winapi critical section in c# code.

First of all, I import functions:

[StructLayout(LayoutKind.Sequential)]
public struct CRITICAL_SECTION { public int dummy; }

// INIT CRITICAL SECTION
[DllImport("kernel32.dll")]
static extern bool InitializeCriticalSectionAndSpinCount(ref CRITICAL_SECTION
   lpCriticalSection, uint dwSpinCount);

// DELETE CRITICAL SECTION
[DllImport("kernel32.dll")]
static extern void DeleteCriticalSection(ref CRITICAL_SECTION
   lpCriticalSection);

// ENTER CRITICAL SECTION
[DllImport("kernel32.dll")]
static extern void EnterCriticalSection(ref CRITICAL_SECTION
   lpCriticalSection);

// LEAVE CRITICAL SECTION
[DllImport("kernel32.dll")]
static extern void LeaveCriticalSection(ref CRITICAL_SECTION
   lpCriticalSection);

In this way I try to use critical section:

static void Main(string[] args)
            {
                GenerateArray();

                InvokeThread invokeThread = () =>
                {
                    WaitForSingleObject(ghSemaphore, 0);
                    EnterCriticalSection(ref CriticalSection);   // critical section

                    int[] array = new int[ARRAY_SIZE_PER_THREAD];
                    int baseI = thread * ARRAY_SIZE_PER_THREAD;
                    for (int i = 0; i < ARRAY_SIZE_PER_THREAD; ++i)
                    {
                        array[i] = gList[baseI + i];
                    }

                    LeaveCriticalSection(ref CriticalSection);       // critical section
                    ReleaseSemaphore(ghSemaphore, 1, IntPtr.Zero);

                    return 0;
                };

                ghSemaphore = CreateSemaphore(ref seqAttr, THREADS_NUMBER, THREADS_NUMBER, "");
                InitializeCriticalSectionAndSpinCount(ref CriticalSection, 0);                           

                IntPtr threadPtr = Marshal.GetFunctionPointerForDelegate(invokeThread);

                IntPtr[] handlers = new IntPtr[THREADS_NUMBER];
                for (int i = 0; i < THREADS_NUMBER; ++i)
                {
                    int handle = CreateThread(IntPtr.Zero, 0, threadPtr, IntPtr.Zero, 0, 0);
                    handlers[i] = new IntPtr(handle);
                }

                WaitForMultipleObjects(THREADS_NUMBER, handlers, true, Infinite);    
                DeleteCriticalSection(ref CriticalSection); // delete critical section
            }
        }

But at next line gList containes wrong values. And every thing is good, if I don't use critical section.

for (int i = 0; i < ARRAY_SIZE_PER_THREAD; ++i)
{
    array[i] = gList[baseI + i];
}

Where could be a problem?

2条回答
时光不老,我们不散
2楼-- · 2019-08-22 09:40

Your definition of the CRITICAL_SECTION struct is wrong. In the Windows headers is has 24 bytes or so, but yours has only 4.

Also, you don't do new CRITICAL_SECTION anywere. And you need it, InitializeCriticalSection sets the data, but don't allocate it.

查看更多
戒情不戒烟
3楼-- · 2019-08-22 09:51

I also agree that using the critical section API from .net seems like a bad design choice. If you must then I'd contemplate writing a C++/CLI mixed-mode wrapper. That way you can include the windows header files directly.

However, there is a rather obvious flaw in your p/invoke code. That's the declaration of the CRITICAL_SECTION struct. You've declared it as holding a single integer value. But the native struct is bigger than that. On x86 it is 24 bytes long, and on x64 it is 40 bytes long. You don't need to declare any of the fields since from your perspective it's just an opaque block of memory.

If I were you I would get rid of CRITICAL_SECTION. I would change all the ref CRITICAL_SECTION parameters to IntPtr. And I would use Marshal.AllocHGlobal(40) to create a large enough block of memory to hold the critical section data.

For example:

[DllImport("kernel32.dll")]
static extern bool InitializeCriticalSectionAndSpinCount(
    IntPtr lpCriticalSection, 
    uint dwSpinCount
);

and so on.

查看更多
登录 后发表回答