2012-10-14 51 views
4

從意見分辨率:
應用程序崩潰是由另一個問題
當文件被鎖定時避免崩潰?

我讀/書面方式從2個不同的應用程序文件,當文件被讀取或寫入造成的,它總是會被鎖定應用程序A或B,他們都使用FileShare.None

我的問題是,即使包裝讀者周圍的try/catch它仍然崩潰與IOException異常在使用在線應用程序(不與writter發生)。

我也取得漁獲作爲catch (IOException ...,我認爲它可以使再沒有其他的區別,使其更具可讀性。

是什麼時,文件被鎖定,並不斷嘗試,直到該文件是可忽略的正確方法是什麼?

while (true) 
{ 
    try 
    { 
     using (FileStream stream = new FileStream("test_file.dat", FileMode.Open, FileAccess.Read, FileShare.None)) 
     { 
      using (TextReader reader = new StreamReader(stream)) 
      { 
       // bla bla bla does not matter 
      } 
     } 
    } 
    catch 
    { 
     // bla bla bla does not matter again 
    } 
    Thread.Sleep(500); 
} 

private bool WriteData(string data) 
{ 
    try 
    { 
     using (FileStream stream = new FileStream("test_file.dat", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)) 
     { 
      stream.SetLength(0); 
      using (TextWriter writer = new StreamWriter(stream)) 
      { 
       writer.Write(data); 
      } 
     } 
     return true; 
    } 
    catch 
    { 
     return false; 
    } 
} 

請注意,當正在使用的文件的任何過程中它讀取或書面方式所以基本上我不會放棄股權(包括作者和讀者使用FileShare.None)任何人我正在處理異常情況,直到文件可用爲止。

+0

,你的意思是,當你運行它「獨立」,它實際上崩潰,或者只是它顯示了拋出異常在調試器? –

+0

獨立運行崩潰「應用程序停止運行」和最後一條錯誤消息我在錯誤日誌文件崩潰之前得到的文件與被鎖定的文件有關:System.IO.IOException:進程無法訪問文件'long_path_of_where_the_file_was',因爲它正在被另一個進程使用。' – Prix

+0

如何使用ReaderWriterLock類? – tazyDevel

回答

5

下面是我們爲此目的而使用的代碼。使用

/// <summary> 
/// Executes the specified action. If the action results in a file sharing violation exception, the action will be 
/// repeatedly retried after a short delay (which increases after every failed attempt). 
/// </summary> 
/// <param name="action">The action to be attempted and possibly retried.</param> 
/// <param name="maximum">Maximum amount of time to keep retrying for. When expired, any sharing violation 
/// exception will propagate to the caller of this method. Use null to retry indefinitely.</param> 
/// <param name="onSharingVio">Action to execute when a sharing violation does occur (is called before the waiting).</param> 
public static void WaitSharingVio(Action action, TimeSpan? maximum = null, Action onSharingVio = null) 
{ 
    WaitSharingVio<bool>(() => { action(); return true; }, maximum, onSharingVio); 
} 

/// <summary> 
/// Executes the specified function. If the function results in a file sharing violation exception, the function will be 
/// repeatedly retried after a short delay (which increases after every failed attempt). 
/// </summary> 
/// <param name="func">The function to be attempted and possibly retried.</param> 
/// <param name="maximum">Maximum amount of time to keep retrying for. When expired, any sharing violation 
/// exception will propagate to the caller of this method. Use null to retry indefinitely.</param> 
/// <param name="onSharingVio">Action to execute when a sharing violation does occur (is called before the waiting).</param> 
public static T WaitSharingVio<T>(Func<T> func, TimeSpan? maximum = null, Action onSharingVio = null) 
{ 
    var started = DateTime.UtcNow; 
    int sleep = 279; 
    while (true) 
    { 
     try 
     { 
      return func(); 
     } 
     catch (IOException ex) 
     { 
      int hResult = 0; 
      try { hResult = (int) ex.GetType().GetProperty("HResult", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(ex, null); } 
      catch { } 
      if (hResult != -2147024864) // 0x80070020 ERROR_SHARING_VIOLATION 
       throw; 
      if (onSharingVio != null) 
       onSharingVio(); 
     } 

     if (maximum != null) 
     { 
      int leftMs = (int) (maximum.Value - (DateTime.UtcNow - started)).TotalMilliseconds; 
      if (sleep > leftMs) 
      { 
       Thread.Sleep(leftMs); 
       return func(); // or throw the sharing vio exception 
      } 
     } 

     Thread.Sleep(sleep); 
     sleep = Math.Min((sleep * 3) >> 1, 10000); 
    } 
} 

例子:

Utilities.WaitSharingVio(
    action:() => 
    { 
     using (var f = File.Open(file, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)) 
     { 
      // ... blah, process the file 
     } 
    }, 
    onSharingVio:() => 
    { 
     Console.WriteLine("Sharing violation. Trying again soon..."); 
    } 
); 
+1

IOException需要使用私有反射來解釋是一件令人遺憾的事情。沒有其他方法可以通過編程的方式找到發生的事情。可怕。 – usr

+0

+1雖然這不完全是我所需要的,但它向我展示了一些有趣的想法,非常感謝。 – Prix

+0

爲什麼使用反射來獲得'HResult'的值,當它是'IOException'的普通屬性? – t3chb0t

-1

使用Reader &作家鎖
正確捕獲的語法

catch(Exception e) 


while (true) 
{ 
    try 
    { 
     using (FileStream stream = new FileStream("test_file.dat", FileMode.Open, FileAccess.Read, FileShare.None)) 
     { 
      using (TextReader reader = new StreamReader(stream)) 
      { 
       // bla bla bla does not matter 
      } 
     } 
    } 
    catch(Exception e) 
    { 
     // bla bla bla does not matter again 
    } 
    Thread.Sleep(500); 
} 
+0

我嘗試過使用異常,IOException,沒有結果。 – Prix

+0

@Prix,我在別處讀到你需要實際指定一個像這個答案所表明的值。我不明白爲什麼(這不是在Python或C++的情況下)。你試過了嗎? –

+1

'catch'和'catch(Exception e)'是等價的,唯一的區別是'Exception'可以在後面的例子中被引用。見[相關答案](http://stackoverflow.com/a/10806013/921321)。 – Lukazoid

1

您可以使用一個互斥對象來保護共享資源,從多個線程或進程同時訪問。

+0

嗯,我不是在尋找解決方法,我想知道爲什麼該方法不能正常工作。 – Prix

+0

我爲您回答問題 - 「文件被鎖定時忽略的正確方法是什麼,並繼續嘗試,直到文件可用? –

+0

那是因爲你忽略了爲什麼我發佈代碼的意義,ofc我發佈代碼的原因是要找出問題是否在其中,以及什麼樣的方式使它適用於我的示例;) – Prix

2

我這樣做從01​​一次使用信息。

我想結果是有點像ReaderWriterLockSlim即在多個進程,而不是線程,被訪問的資源的情況下工作。

+0

+ 1感謝您的參考,這是很不錯的閱讀 – Prix

1

您可以通過編寫如下功能檢查您的文件鎖定:

protected bool IsFileLocked(FileInfo file) 
{ 
    FileStream stream = null; 

    try 
    { 
     stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None); 
    } 
    catch (IOException) 
    { 
     //the file is unavailable because it is: 
     //still being written to 
     //or being processed by another thread 
     //or does not exist (has already been processed) 
     return true; 
    } 
    finally 
    { 
     if (stream != null) 
      stream.Close(); 
    } 

    //file is not locked 
    return false; 
} 
+1

問題是函數不是很有幫助,它可以返回false,但在'Close()'和調用者檢查布爾值的時間之間,另一個進程可能已經打開它用於寫作。 –

+0

我真的沒有看到使用這個差異,我的意思是使用只會打開文件,如果它是可用的權利?如果不是,它會拋出,因此我正在避免關閉/最終需要。 – Prix

+0

'FileNotFoundException'怎麼樣?它從'IOException'繼承而來,意味着完全不同的東西。 – gimbar

0

從Timwi答案(在另一種情況下雖然)幫助我們很多,但我發現標誌「BindingFlags.Public」也需要,如果你想加入從所有的IOExceptions獲取HRESULT:當你說「死機」

public static int GetHresult(this IOException ex) 
{ 
    return (int)ex.GetType().GetProperty("HResult", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetValue(ex, null); 
}