0

全部。我需要在 c# 代码中使用 winapi 关键部分。

首先,我导入函数:

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

通过这种方式,我尝试使用临界区:

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

但在下一行 gList 包含错误的值。如果我不使用关键部分,每件事都很好。

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

哪里可能有问题?

4

2 回答 2

2

我也同意使用 .net 中的关键部分 API 似乎是一个糟糕的设计选择。如果必须的话,我会考虑编写一个 C++/CLI 混合模式包装器。这样您就可以直接包含 windows 头文件。

但是,您的 p/invoke 代码中有一个相当明显的缺陷。那是CRITICAL_SECTION结构的声明。您已将其声明为持有一个整数值。但本机struct比这更大。在 x86 上它是 24 字节长,而在 x64 上它是 40 字节长。您不需要声明任何字段,因为从您的角度来看,它只是一个不透明的内存块。

如果我是你,我会摆脱CRITICAL_SECTION. 我会将所有ref CRITICAL_SECTION参数更改为IntPtr. 我会用它Marshal.AllocHGlobal(40)来创建足够大的内存块来保存临界区数据。

例如:

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

等等。

于 2013-01-04T12:07:24.333 回答
2

您对CRITICAL_SECTION结构的定义是错误的。在 Windows 标头中有 24 个字节左右,但你的只有 4 个。

另外,你什么都不做new CRITICAL_SECTION。你需要它,InitializeCriticalSection设置数据,但不要分配它。

于 2013-01-04T12:07:38.640 回答