You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在两个独立Python解释器间传递锁?Windows锁文件异常排查

Why Your Cross-Process File Lock Isn't Working (Windows Python 3.6)

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:

  1. msvcrt.LK_LOCK is a shared lock, not exclusive—so other processes can still open and read the file
  2. 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
  3. If you wanted to block file opening entirely, you'd need os.O_EXCL (but that only works with os.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

火山引擎 最新活动