2010-09-27 2 views
6

Tôi cần mở tệp nhưng nếu nó hiện không khả dụng, tôi cần đợi cho đến khi nó sẵn sàng. Cách tiếp cận tốt nhất để thực hiện là gì?Mẫu thích hợp để chờ khóa tệp được phát hành là gì?

SCENARIO

Tôi đang sử dụng các file như một cơ chế bộ nhớ đệm dai dẳng cho dữ liệu ứng dụng. Dữ liệu này cần phải được đọc và deserialized thường xuyên (chỉ viết một lần, và thỉnh thoảng bị xóa). Tôi có một quá trình dọn dẹp chạy trên một chuỗi riêng biệt để xác định những tệp nào không còn cần thiết và xóa chúng nữa. Mở và đọc các tập tin có thể xảy ra đồng thời (hiếm khi, nhưng có thể xảy ra) và tôi muốn quá trình chờ và cố gắng đọc lại dữ liệu.

Cảm ơn!

+0

đây có phải là tất cả các nơi tham gia trong một quá trình duy nhất? –

+0

có. Nó thực sự đang được sử dụng trên Windows 7 Phone (Silverlight). – Micah

Trả lời

0

Giống như tất cả câu hỏi "phương pháp tiếp cận tốt nhất" là gì, điều này phụ thuộc vào nhu cầu của bạn. Một số tùy chọn mà đến một cách dễ dàng để ghi nhớ:

  1. Abort nỗ lực
  2. Vòng đến khi tập tin bị mở khóa
  3. Hãy hỏi người dùng phải làm gì về nó

Mà một trong những bạn chọn phụ thuộc vào cách bạn có thể đối phó với nó.

+1

1) không phải là phương tiện rất hiệu quả để chờ khóa có sẵn. 3) không phải là một giải pháp có lập trình, mặc dù có lẽ là giải pháp đúng. Và 2) là, tôi nghi ngờ, giải pháp mặc định mà Micah đang cố gắng cải thiện. –

+0

Tôi sẽ chỉ ra rằng câu hỏi ban đầu trông không giống như hình thức hiện tại của nó. – Tergiver

2

Tùy thuộc vào người kiểm soát tệp. Nếu một phần của ứng dụng của bạn cần đợi cho đến khi một phần khác của ứng dụng hoàn tất việc chuẩn bị tệp, thì bạn có thể sử dụng ManualResetEvent. Nghĩa là, lúc khởi động, chương trình của bạn sẽ tạo ra một sự kiện mới:

public ManualResetEvent FileEvent = new ManualResetEvent(false);

Bây giờ, một phần của chương trình đó là chờ đợi cho các tập tin có mã này:

FileEvent.WaitOne();

Và phần của chương trình tạo tệp này khi tệp sẵn sàng:

FileEvent.Set();

Nếu ứng dụng của bạn phải chờ một tập tin đang được sử dụng bởi một ứng dụng khác mà bạn không có quyền kiểm soát, giải pháp thực sự duy nhất của bạn là liên tục thử mở tập tin.

FileStream f = null; 
while (f == null) 
{ 
    try 
    { 
     f = new FileStream(...); 
    } 
    catch (IOException) 
    { 
     // wait a bit and try again 
     Thread.Sleep(5000); 
    } 
} 

Tất nhiên, có thể bạn sẽ không muốn bắt vô điều kiện IOException. Bạn có thể muốn nắm bắt các trường hợp ngoại lệ cụ thể cụ thể mà bạn biết cách xử lý (ví dụ: bạn sẽ không muốn thử lại nếu bạn có số DirectoryNotFoundException). Các hàm I/O ghi lại các ngoại lệ mà chúng dự kiến ​​sẽ ném, và trong hoàn cảnh nào.

9

Tôi không phải là một fan hâm mộ lớn của try/catch IOException vì:

  1. Lý do cho sự ngoại lệ là không rõ.
  2. Tôi không thích các ngoại lệ 'được mong đợi' như tôi thường chạy với sự bẻ khóa.

Bạn có thể làm điều này mà không ngoại lệ bằng cách gọi CreateFile và trả về một dòng khi/nếu nó cuối cùng trả về một tay cầm:

public static System.IO.Stream WaitForExclusiveFileAccess(string filePath, int timeout) 
{ 
    IntPtr fHandle; 
    int errorCode; 
    DateTime start = DateTime.Now; 

    while(true) 
    { 
     fHandle = CreateFile(filePath, EFileAccess.GenericRead | EFileAccess.GenericWrite, EFileShare.None, IntPtr.Zero, 
          ECreationDisposition.OpenExisting, EFileAttributes.Normal, IntPtr.Zero); 

     if (fHandle != IntPtr.Zero && fHandle.ToInt64() != -1L) 
      return new System.IO.FileStream(fHandle, System.IO.FileAccess.ReadWrite, true); 

     errorCode = Marshal.GetLastWin32Error(); 

     if (errorCode != ERROR_SHARING_VIOLATION) 
      break; 
     if (timeout >= 0 && (DateTime.Now - start).TotalMilliseconds > timeout) 
      break; 
     System.Threading.Thread.Sleep(100); 
    } 


    throw new System.IO.IOException(new System.ComponentModel.Win32Exception(errorCode).Message, errorCode); 
} 

#region Win32 
const int ERROR_SHARING_VIOLATION = 32; 

[Flags] 
enum EFileAccess : uint 
{ 
    GenericRead = 0x80000000, 
    GenericWrite = 0x40000000 
} 

[Flags] 
enum EFileShare : uint 
{ 
    None = 0x00000000, 
} 

enum ECreationDisposition : uint 
{ 
    OpenExisting = 3, 
} 

[Flags] 
enum EFileAttributes : uint 
{ 
    Normal = 0x00000080, 
} 

[DllImport("kernel32.dll", EntryPoint = "CreateFileW", SetLastError = true, CharSet = CharSet.Unicode)] 
static extern IntPtr CreateFile(
    string lpFileName, 
    EFileAccess dwDesiredAccess, 
    EFileShare dwShareMode, 
    IntPtr lpSecurityAttributes, 
    ECreationDisposition dwCreationDisposition, 
    EFileAttributes dwFlagsAndAttributes, 
    IntPtr hTemplateFile); 

#endregion 
2

Phiên bản chung chung hơn của phương pháp của csharptest.net có thể giống như thế này (cũng có thể, sử dụng SafeFileHandle và loại bỏ ngoại lệ ném vào thời gian chờ, bạn có thể nhận các giá trị enum tại http://www.pinvoke.net/default.aspx/kernel32.createfile):

public static FileStream WaitForFileAccess(string filePath, FileMode fileMode, FileAccess access, FileShare share, TimeSpan timeout) 
    { 
     int errorCode; 
     DateTime start = DateTime.Now; 

     while (true) 
     { 
      SafeFileHandle fileHandle = CreateFile(filePath, ConvertFileAccess(access), ConvertFileShare(share), IntPtr.Zero, 
                ConvertFileMode(fileMode), EFileAttributes.Normal, IntPtr.Zero); 

      if (!fileHandle.IsInvalid) 
      { 
       return new FileStream(fileHandle, access); 
      } 

      errorCode = Marshal.GetLastWin32Error(); 

      if (errorCode != ERROR_SHARING_VIOLATION) 
      { 
       break; 
      } 

      if ((DateTime.Now - start) > timeout) 
      { 
       return null; // timeout isn't an exception 
      } 

      Thread.Sleep(100); 
     } 

     throw new IOException(new Win32Exception(errorCode).Message, errorCode); 
    } 

    private static EFileAccess ConvertFileAccess(FileAccess access) 
    { 
     return access == FileAccess.ReadWrite ? EFileAccess.GenericRead | EFileAccess.GenericWrite : access == FileAccess.Read ? EFileAccess.GenericRead : EFileAccess.GenericWrite; 
    } 

    private static EFileShare ConvertFileShare(FileShare share) 
    { 
     return (EFileShare) ((uint) share); 
    } 

    private static ECreationDisposition ConvertFileMode(FileMode mode) 
    { 
     return mode == FileMode.Open ? ECreationDisposition.OpenExisting : mode == FileMode.OpenOrCreate ? ECreationDisposition.OpenAlways : (ECreationDisposition) (uint) mode; 
    } 

    [DllImport("kernel32.dll", EntryPoint = "CreateFileW", SetLastError = true, CharSet = CharSet.Unicode)] 
    private static extern SafeFileHandle CreateFile(
     string lpFileName, 
     EFileAccess dwDesiredAccess, 
     EFileShare dwShareMode, 
     IntPtr lpSecurityAttributes, 
     ECreationDisposition dwCreationDisposition, 
     EFileAttributes dwFlagsAndAttributes, 
     IntPtr hTemplateFile);