为啥 .Net 代码版本挂起,但 C++ 没有?

Posted

技术标签:

【中文标题】为啥 .Net 代码版本挂起,但 C++ 没有?【英文标题】:Why .Net code version hangs, but C++ not?为什么 .Net 代码版本挂起,但 C++ 没有? 【发布时间】:2015-07-28 22:11:54 【问题描述】:

为什么 .Net (C#) 中的相同代码会挂起,但在 C++ 中却不是?真正的问题在于 System.IO.FileStream,但我将其简化为 CreateFile,但它仍然挂在 .Net 中(同时几乎 100% 类似于 C++ 代码):

using System;
using System.Runtime.InteropServices;
using System.Threading;

namespace ConsoleApplication1

    class Program
    
        static IntPtr _handle;

        static void Main()
        
            _handle = CreateFile("test.txt", GENERIC_READ | GENERIC_WRITE,
                FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero,
                OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);

            new Thread(MyProc)  IsBackground = true .Start();
            new Thread(MyProc)  IsBackground = true .Start();

            Console.ReadKey();
        

        static void MyProc()
        
            for (int i = 0; i < int.MaxValue; i++)
            
                Console.WriteLine(i);

                var _overlapped = new NativeOverlapped();

                LockFileEx(_handle, LOCKFILE_EXCLUSIVE_LOCK, 0, int.MaxValue, int.MaxValue, ref _overlapped);

                Thread.Sleep(50);

                UnlockFileEx(_handle, 0, int.MaxValue, int.MaxValue, ref _overlapped);
            
        

        public const int LOCKFILE_EXCLUSIVE_LOCK = 0x00000002;

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool LockFileEx(IntPtr hFile, int flags, int reserved, int numberOfBytesToLockLow, int numberOfBytesToLockHigh,
            ref NativeOverlapped overlapped);

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool UnlockFileEx(IntPtr hFile, int reserved, int numberOfBytesToUnlockLow, int numberOfBytesToUnlockHigh,
            ref NativeOverlapped overlapped);

        public const int GENERIC_READ = unchecked((int)0x80000000L);
        public const int GENERIC_WRITE = (int)0x40000000L;

        public const int OPEN_ALWAYS = 4;

        public const int FILE_SHARE_READ = 0x00000001;
        public const int FILE_SHARE_WRITE = 0x00000002;
        public const int FILE_SHARE_DELETE = 0x00000004;

        public const int FILE_ATTRIBUTE_NORMAL = 0x00000080;

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern IntPtr CreateFile(string fileName, int desiredAccess, int shareMode,
            IntPtr pSecurityAttributes, int creationDisposition, int flagsAndAttributes, IntPtr hTemplateFile);
    

这是不挂起的C++版本:

#include "stdafx.h"

#include <thread>
#include <Windows.h>

HANDLE hFile;

DWORD WINAPI MyProc(LPVOID lpParam);

int _tmain(int argc, _TCHAR* argv[])

    hFile = CreateFile(L"test.txt", GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
        OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);  

    HANDLE hThread1 = CreateThread(NULL, 0, MyProc, NULL, 0, NULL);
    HANDLE hThread2 = CreateThread(NULL, 0, MyProc, NULL, 0, NULL);

    int ch = getchar();

    return 0;


DWORD WINAPI MyProc(LPVOID lpParam)

    for(int i = 0; i < INT_MAX; i++)
    
        printf("%d\r\n", i);

        OVERLAPPED overlapped = 0;

        LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, INT_MAX, INT_MAX, &overlapped);

        Sleep(50);

        UnlockFileEx(hFile, 0, INT_MAX, INT_MAX, &overlapped);
    

    return 0;

【问题讨论】:

反对者,请解释! 似乎挂起的事实取决于配置中的某些东西。我的就是这样一个:Net 4.5、VS 2012 Express、Win7 64bit、i7-3610QM。 .Net 变体在控制台中以两个零挂起 100%。很少会到 20,然后仍然挂起。 您是否尝试使用 Debug->Windows->Threads 暂停并检查每个线程挂起时的位置? (假设您使用的是 Visual Studio) @o_weisman 是的。 Thread1 在 LockFileEx 上,Thread2 在 UnlockFileEx 上。我试图删除文件,任意更改锁定文件范围并重建项目 - 现在我的演示项目可以工作,但不是那么直接 - 在相同的操作之后它再次挂起。尽管重建和文件删除,我的工作项目(使用 FileStream)仍然经常出现问题。甚至不要猜测它会是什么,进一步检查。 为什么 C# 程序与 C++ 程序的行为不同通常很容易解释,死锁对时间非常敏感,当然时间也不一样。然而,你不能忽视房间里的大象,这段代码不应该死锁。其他人也非常非常不可能复制它。你需要修复你的机器,这总是从程序员自愿安装在他们机器上的那种收缩包装的恶意软件开始。禁用您的反恶意软件扫描程序和任何云存储实用程序,如 Dropbox 等。 【参考方案1】:

在同一个文件句柄上使用 LOCKFILE_EXCLUSIVE_LOCK 调用 LockFileEx 两次似乎是无效的。它必须是不同的文件句柄或句柄的某种进程内同步。

令人不安的是,MSDN 文档中的问题并没有明确的含义。并且在运行时没有明确的错误消息。更重要的是,在我看来,这个问题只在我的电脑上重现。这有点丑。

【讨论】:

我想知道谁否决了这个正确答案?我肯定确认问题出在同一文件句柄上调用 LockFileEx(LOCKFILE_EXCLUSIVE_LOCK) 两次(甚至更多)。我的工作项目,因为我在文件句柄共享线程之间添加了进程内同步,现在工作稳定了。和以前一样,它稳定地挂起。所以,downvoters,请解释一下!你投反对票的原因是什么?我对你的这种行为感到非常震惊。 问题是LockFileEx文档中必须明确说明,并且要明确运行时错误,而不是概率的事情而是要确定的。

以上是关于为啥 .Net 代码版本挂起,但 C++ 没有?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我通过 spawn() 创建的 Node 子进程挂起?

AWX 所有作业都停止处理并无限期挂起——为啥

c++ std::thread 与静态成员挂起

为啥我与 Apple APNS 的 TCP 连接挂起并强行断开连接

Visual Studio 2013 C++ 本机代码中与互锁操作的线程同步挂起

为啥执行某些命令后 Pexpect 会间歇性挂起(未检测到 EOF)?