2013-02-14 53 views
1

我有兩個.NET 4.0 WinForms應用程序(主要)從共享MemoryMappedFiles中讀取地理數據。最終用戶可以自由地啓動應用程序或其他應用程序,或者同時運行這兩個應用程序。第一個打開的應用程序創建命名的MemoryMapFile-s,第二個打開已存在的應用程序。 但是,打開現有的名爲MemoryMappedFile似乎是不可靠的。它可以處理大約80%的案例,但大約有20%的案例使用FileNotFoundException失敗。 症狀不安全可重現,它失敗時,它似乎是純粹的運氣,當它成功。與MemoryMappedFile的不可靠連接

下面是使用這兩個應用得到MemoryMappedFiles代碼:

private static MemoryMappedFile GetMemoryMappedFile(string filePath) 
{ 
    string mapName = filePath; // I have also tried here @"Global\myfile", no difference 
    MemoryMappedFile mmf = null; 
    try 
    { 
     // When the first app executes this step, it always succeeds. 
     // When the second app comes here, it fails as it should. 
     mmf = MemoryMappedFile.CreateFromFile(filePath, FileMode.OpenOrCreate, 
           mapName, HundredMB, MemoryMappedFileAccess.ReadWrite); 
    } 
    catch (IOException) 
    { 
     try 
     { 
      // Opening the already existing named MemoryMappedFile by the SECOND app. 
      // This line fails about 20% of the time. 
      mmf = MemoryMappedFile.OpenExisting(mapName, 
            MemoryMappedFileRights.ReadWrite); 
     } 
     catch (FileNotFoundException ex) 
     { 
      Console.WriteLine("Yet again, could not open MMF. Life sux."); 
     } 
    } 
    return mmf; 
} 

回答

0

我是這個問題的作者,我是一個傻瓜。 MMF的工作原理如下。失敗是由第二種方法(很久以前我寫的)造成的,我創建了MMF-s,但是我在那裏應用了不同的MMF命名約定。問題中的代碼失敗(正確),因爲一些MMF-s是在我的第二個方法中以錯誤名稱創建的。

我不會刪除這個問題,因爲仍然有價值的答案可以幫助後來的讀者貢獻。非常感謝所有花時間回答的人。

2

你所描述什麼似乎是一個完美的疑似病例。你有什麼是競賽條件。例如,在CreateFromFile上拋出異常的代碼告訴你文件存在。在此代碼達到OpenExisting之前,另一個進程將關閉該文件。然後調用OpenExisting並失敗,因爲該文件不再存在。這是您需要編寫代碼的有效案例。

+0

雖然你在技術上是正確的,但這不可能是真正的問題。這些應用程序在啓動時創建/打開MMF,並在它們被殺時關閉它們。此外,這些應用程序的開放時間長。總是第二個失敗,當它被打開。 – user256890 2013-02-14 16:47:28

+0

那麼,你真的沒有提供任何信息來暗示你的代碼沒有出錯和那個。NET Framework的MemoryMappedFile實現有缺陷或沒有正確記錄。你所描述的一切都表明你比其他任何事情都具有競爭條件。 – 2013-02-14 17:20:08

+0

當出現FileNotFoundException時,嘗試使用'File.Exists'來查看它是否返回'false'。這是另一種競爭條件;但如果你的問題確實是一種競爭條件,那麼它應該是'false'而不是'true',並且有20%的失敗率。如果它總是「真」,那麼是的,你的代碼中可能沒有競爭條件。 – 2013-02-14 17:23:18

1

翻轉邏輯怎麼樣?由於MMF的創建應該是一次性的,但現有的開放應該是更常見的情況,也許這會起作用?

private static MemoryMappedFile GetMemoryMappedFile(string filePath) 
{ 
    var mapName = filePath; // I have also tried here @"Global\myfile", no difference 
    MemoryMappedFile mmf = null; 

    try 
    { 
     // When the first app executes this step, it fails as it should. 
     // Opening the already existing named MemoryMappedFile by the SECOND app. 
     mmf = MemoryMappedFile.OpenExisting(mapName, MemoryMappedFileRights.ReadWrite); 
    } 
    catch (FileNotFoundException) 
    { 
     try 
     { 
      // When the first app executes this step, it always succeeds. 
      mmf = MemoryMappedFile.CreateFromFile(
       filePath, 
       FileMode.OpenOrCreate, 
       mapName, 
       HundredMB, 
       MemoryMappedFileAccess.ReadWrite); 
     } 
     catch (IOException ex) 
     { 
      Console.Error.WriteLine("Yet again, could not open MMF. Life sux: " + ex); 
     } 
    } 

    return mmf; 
} 
+0

我也注意到,如果我使用'@「Global \ myfile」'作爲'mapName',我必須以管理員身份運行,否則我會得到'UnauthorizedAccessException'。 – 2013-02-14 19:12:41

3
string mapName = filePath; 

這看起來很腥根本。 文件路徑字符串應始終引用該文件的路徑名絕對。像「c:\ foo \ bar \ baz.bin」一樣,永遠不會有相對的文件名,比如「baz.bin」。像「Global \ myfile」這樣的名稱不是路徑名,它是一個內存映射名稱。這是另一個進程用來打開映射的地方,它不知道或關心充當地圖的後備存儲的實際文件。

這將失敗的一種非常常見的方式是當您的程序的工作目錄(Environment.CurrentDirectory)未設置在您希望設置的位置。除了突然發現CreateFromFile()調用失敗之外,它還有一個擅長更改的技巧。

因此,通過選擇一個獨特的地圖名稱來解決您的問題,該地圖名稱對於您的應用程序非常具體。還有一個您用完整路徑名選擇的文件。通常存儲在AppData中,您可以在沒有UAC提升的情況下寫入文件夾。