如何在两个独立Python解释器间传递锁?Windows锁文件异常排查
Hey, let's break down why your current code isn't blocking the second Python interpreter from accessing the file, and walk through the fixes you need.
Core Issues in Your Original Code
Your approach has two key problems tied to how Windows handles file locking:
1. You're Using a Shared Lock, Not an Exclusive One
The msvcrt.LK_LOCK flag you're using is a blocking shared lock, not an exclusive lock. Here's what that means:
- Other processes can still open the file in read-write mode
- They'll only hit an error if they try to write to the locked byte range—not when opening the file itself
- If you want to block all other processes from accessing the file (or the locked range), you need to use an exclusive lock flag instead.
2. Windows File Locks Are Byte-Range Based
Windows doesn't lock the entire file by default—your msvcrt.locking call only locks the first 4096 bytes. Even with an exclusive lock, other processes can still open the file and access bytes outside that range. If you want to block access to the whole file, you need to lock the entire file size (or a range larger than the file).
Fixed Solutions
Solution 1: Use an Exclusive Byte-Range Lock
This will block other processes from writing (or reading, if you use the right flag) the locked range. Here's how to adjust your code:
Process 1 (Lock Holder):
import os import mmap import msvcrt file_path = r'C:\somefile' # Open file with create + read-write permissions fd = os.open(file_path, os.O_CREAT | os.O_RDWR) try: # Use LK_NBLCK for non-blocking exclusive lock (fails immediately if locked) # Use LK_LOCK if you want to block until the lock is available msvcrt.locking(fd, msvcrt.LK_NBLCK, 4096) # Create memory map mm_file = mmap.mmap(fd, 4096, access=mmap.ACCESS_WRITE) mm_file.write(b"Locked data from Process 1") mm_file.flush() # Keep lock held until user input input("Press Enter to release lock...") finally: # Release the lock and clean up msvcrt.locking(fd, msvcrt.LK_UNLCK, 4096) os.close(fd)
Process 2 (Lock Attempter):
import os import msvcrt file_path = r'C:\somefile' fd = os.open(file_path, os.O_RDWR) try: # Try to acquire exclusive lock (non-blocking) try: msvcrt.locking(fd, msvcrt.LK_NBLCK, 4096) print("Lock acquired successfully!") # Read and verify data os.lseek(fd, 0, os.SEEK_SET) print(f"File content: {os.read(fd, 4096)}") msvcrt.locking(fd, msvcrt.LK_UNLCK, 4096) except OSError as e: print(f"Failed to acquire lock (file is locked): {e}") finally: os.close(fd)
With this setup, Process 2 will fail to acquire the exclusive lock immediately if Process 1 holds it. If you use LK_LOCK instead of LK_NBLCK, Process 2 will block until the lock is released.
Solution 2: Use a Windows Named Mutex (More Reliable for Process Sync)
If your goal is process-level mutual exclusion (not just protecting a file range), a Windows named mutex is a better choice—it's designed explicitly for cross-process synchronization.
Process 1 (Mutex Creator/Holder):
import ctypes from ctypes import wintypes import os import mmap # Load Windows kernel32 API kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) # Define API signatures kernel32.CreateMutexW.argtypes = [wintypes.LPCVOID, wintypes.BOOL, wintypes.LPCWSTR] kernel32.CreateMutexW.restype = wintypes.HANDLE kernel32.WaitForSingleObject.argtypes = [wintypes.HANDLE, wintypes.DWORD] kernel32.WaitForSingleObject.restype = wintypes.DWORD kernel32.ReleaseMutex.argtypes = [wintypes.HANDLE] kernel32.ReleaseMutex.restype = wintypes.BOOL kernel32.CloseHandle.argtypes = [wintypes.HANDLE] kernel32.CloseHandle.restype = wintypes.BOOL # Create a unique named mutex (use "Global\\YourName" for cross-user sessions) mutex_name = "MyPythonCrossProcessMutex" h_mutex = kernel32.CreateMutexW(None, False, mutex_name) if not h_mutex: raise ctypes.WinError(ctypes.get_last_error()) try: # Wait indefinitely to acquire the mutex wait_result = kernel32.WaitForSingleObject(h_mutex, 0xFFFFFFFF) if wait_result != 0: # WAIT_OBJECT_0 means success raise RuntimeError(f"Failed to acquire mutex: {wait_result}") # Now safely access your shared file/mmap fd = os.open(r'C:\somefile', os.O_CREAT | os.O_RDWR) mm_file = mmap.mmap(fd, 4096, access=mmap.ACCESS_WRITE) mm_file.write(b"Data protected by mutex") mm_file.flush() input("Press Enter to release mutex and exit...") finally: # Cleanup kernel32.ReleaseMutex(h_mutex) kernel32.CloseHandle(h_mutex) os.close(fd)
Process 2 (Mutex Attempter):
import ctypes from ctypes import wintypes import os import mmap kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) kernel32.OpenMutexW.argtypes = [wintypes.DWORD, wintypes.BOOL, wintypes.LPCWSTR] kernel32.OpenMutexW.restype = wintypes.HANDLE kernel32.WaitForSingleObject.argtypes = [wintypes.HANDLE, wintypes.DWORD] kernel32.WaitForSingleObject.restype = wintypes.DWORD kernel32.ReleaseMutex.argtypes = [wintypes.HANDLE] kernel32.ReleaseMutex.restype = wintypes.BOOL kernel32.CloseHandle.argtypes = [wintypes.HANDLE] kernel32.CloseHandle.restype = wintypes.BOOL # Open the existing mutex mutex_name = "MyPythonCrossProcessMutex" h_mutex = kernel32.OpenMutexW(0x1F0001, False, mutex_name) # Full access rights if not h_mutex: raise ctypes.WinError(ctypes.get_last_error()) try: # Wait up to 5 seconds for the mutex wait_result = kernel32.WaitForSingleObject(h_mutex, 5000) if wait_result == 0: print("Mutex acquired! Accessing shared data...") fd = os.open(r'C:\somefile', os.O_RDWR) mm_file = mmap.mmap(fd, 4096, access=mmap.ACCESS_READ) print(f"Shared data: {mm_file.read(4096)}") mm_file.close() os.close(fd) kernel32.ReleaseMutex(h_mutex) elif wait_result == 0x00000102: # WAIT_TIMEOUT print("Timeout waiting for mutex (process 1 still holds it)") else: raise RuntimeError(f"Mutex wait failed: {wait_result}") finally: kernel32.CloseHandle(h_mutex)
This approach is more robust for process synchronization because it's not tied to file operations—you can use it to protect any critical section across processes, not just file access.
Recap of Why Your Original Code Failed
To sum it up:
msvcrt.LK_LOCKis a shared lock, not exclusive—so other processes can still open and read the file- Windows file locks are byte-range specific, not full-file locks—opening the file doesn't trigger an error, only writing to the locked range does
- If you wanted to block file opening entirely, you'd need
os.O_EXCL(but that only works withos.O_CREAT, which isn't useful for existing files)
Pick the solution that matches your actual use case: byte-range locks for file-specific protection, or named mutexes for general process synchronization.
内容的提问来源于stack exchange,提问作者Kevin S




