Межпроцессная блокировка Reader Writer (или дескрипторы файлов и доступ запрещен) - PullRequest
0 голосов
/ 08 мая 2009

Хорошо, сначала немного фона. Нам была нужна межпроцессная блокировка чтения / записи. Мы решили использовать файл и заблокировать первый байт, используя LockEx и UnlockEx. Класс создает файл в системной временной папке при его создании. Файл создан с правами чтения и записи и совместного использования чтения | записи | удаления. Мы также указываем DeleteOnClose, чтобы не оставлять множество временных файлов. Очевидно, что AcquireReader и AcquireWriter вызывают LockEx с соответствующими флагами, а ReleaseLock вызывает UnlockEx.
Мы проверили этот класс с помощью небольшого приложения, в котором можно запустить несколько экземпляров, и оно отлично работает. У приложения, которое его использует, есть проблема, которую нам удалось воспроизвести в другом небольшом тестовом приложении. В псевдокоде это

Create InterProcessReaderWriter
Dispose InterProcessReaderWriter without acquiring any locks
Launch a child process which takes a reader lock

При первом запуске это работает нормально. Если вы попытаетесь запустить его снова, пока дочерний процесс с первого раза все еще удерживает блокировку, мы получаем исключение UnauthorisedAccessException при попытке открыть файл.
Похоже, это проблема с правами доступа, а не нарушение общего доступа, но все процессы в этом тестовом примере выполняются как один и тот же пользователь. У кого-нибудь есть идеи?

Я заметил другой вопрос, который предлагает использовать мьютекс и семафор для достижения того, что мы хотим. Я мог бы изменить нашу реализацию, но я все еще хотел бы знать, что вызывает эту проблему.

Ответы [ 2 ]

0 голосов
/ 05 июня 2009

Почему бы не заблокировать весь файл? а не 1-й байт. Преимущество приведенного ниже примера класса заключается в том, что он может работать между процессами на разных компьютерах, а не ограничиваться одним компьютером.

Вот простой пример использования класса lockfilehelper ниже.

Module Module1
    Sub Main()
        Using lockFile As New LockFileHelper("\\sharedfolder\simplefile.lock")
            If lockFile.LockAcquire(1000) Then
                ' Do your work here.
            Else
                ' Manage timeouts here.
            End If
        End Using
    End Sub
End Module

Вот код для класса Helper.

Public Class LockFileHelper
    Implements IDisposable
    '-------------------------------------------------------------------------------------------------
    ' We use lock files in various places in the system to provide a simple co-ordination mechanism
    ' between different threads within a process and for sharing access to resources with the same
    ' process running across different machines.
    '-------------------------------------------------------------------------------------------------
    Private _lockFileName As String
    Private _ioStream As IO.FileStream
    Private _acquiredLock As Boolean = False
    Private _wasLocked As Boolean = False
    Public Sub New(ByVal LockFileName As String)
        _lockFileName = LockFileName
    End Sub
    Public ReadOnly Property LockFileName() As String
        Get
            Return _lockFileName
        End Get
    End Property
    Public ReadOnly Property WasLocked() As Boolean
        Get
            Return _wasLocked
        End Get
    End Property
    Public Function Exists() As Boolean
        Return IO.File.Exists(_lockFileName)
    End Function
    Public Function IsLocked() As Boolean
        '-------------------------------------------------------------------------------------------------
        ' If this file already locked?
        '-------------------------------------------------------------------------------------------------
        Dim Result As Boolean = False
        Try
            _ioStream = IO.File.Open(_lockFileName, IO.FileMode.Create, IO.FileAccess.ReadWrite, IO.FileShare.None)
            _ioStream.Close()
        Catch ex As System.IO.IOException
            ' File is in used by another process.
            Result = True
        Catch ex As Exception
            Throw ex
        End Try

        Return Result
    End Function
    Public Sub LockAcquireWithException(ByVal TimeOutMilliseconds As Int32)
        If Not LockAcquire(TimeOutMilliseconds) Then
            Throw New Exception("Timed out trying to acquire a lock on the file " & _lockFileName)
        End If
    End Sub
    Public Function LockAcquire(ByVal TimeOutMilliseconds As Int32) As Boolean
        '-------------------------------------------------------------------------------------------------
        ' See have we already acquired the lock. THis can be useful in situations where we are passing
        ' locks around to various processes and each process may want to be sure it has acquired the lock.
        '-------------------------------------------------------------------------------------------------
        If _acquiredLock Then
            Return _acquiredLock
        End If

        _wasLocked = False
        Dim StartTicks As Int32 = System.Environment.TickCount
        Dim TimedOut As Boolean = False
        If Not IO.Directory.Exists(IO.Path.GetDirectoryName(_lockFileName)) Then
            IO.Directory.CreateDirectory(IO.Path.GetDirectoryName(_lockFileName))
        End If
        Do
            Try
                _ioStream = IO.File.Open(_lockFileName, IO.FileMode.Create, IO.FileAccess.ReadWrite, IO.FileShare.None)
                _acquiredLock = True
            Catch ex As System.IO.IOException
                ' File is in used by another process.
                _wasLocked = True
                Threading.Thread.Sleep(100)
            Catch ex As Exception
                Throw ex
            End Try
            TimedOut = ((System.Environment.TickCount - StartTicks) >= TimeOutMilliseconds)
        Loop Until _acquiredLock OrElse TimedOut
        '-------------------------------------------------------------------------------------------------
        ' Return back the status of the lock acquisition.
        '-------------------------------------------------------------------------------------------------
        Return _acquiredLock
    End Function
    Public Sub LockRelease()
        '-------------------------------------------------------------------------------------------------
        ' Release the lock (if we got it in the first place)
        '-------------------------------------------------------------------------------------------------
        If _acquiredLock Then
            _acquiredLock = False
            If Not IsNothing(_ioStream) Then
                _ioStream.Close()
                _ioStream = Nothing
            End If
        End If
    End Sub
    Private disposedValue As Boolean = False        ' To detect redundant calls
    ' IDisposable
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                Call LockRelease()
            End If

            ' TODO: free shared unmanaged resources
        End If
        Me.disposedValue = True
    End Sub
#Region " IDisposable Support "
    ' This code added by Visual Basic to correctly implement the disposable pattern.
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
#End Region
End Class
0 голосов
/ 10 мая 2009

Мне кажется, что дочерний процесс пытается получить доступ к одному и тому же файлу дважды.

уникальны ли имена временных файлов?

...