2

共有MemoryMappedFilesから地理データを(主に)読み取る2つの.NET4.0WinFormsアプリケーションがあります。エンドユーザーは、どちらかのアプリを自由に起動したり、両方を同時に実行したりできます。最初に開かれたアプリは名前付きのMemoryMapFile-sを作成し、2番目のアプリは既存のアプリを開きます。ただし、既存の名前付きMemoryMappedFileを開くのは信頼できないようです。ケースの約80%で機能しますが、FileNotFoundExceptionで失敗するケースの約20%です。症状は安全に再現することはできません。失敗したときと成功したときは幸運のようです。

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;
}
4

4 に答える 4

3

あなたが説明したことは、完全に可能性の高いケースのようです。あなたが持っているのは競合状態です。たとえば、例外をスローするコードはCreateFromFile、ファイルが存在することを示しています。次に、このコードが に到達する前に、別のプロセスがファイルを閉じますOpenExistingOpenExistingが呼び出され、ファイルが存在しないため失敗します。これは、コードを記述する必要がある有効なケースです。

于 2013-02-14T16:40:22.480 に答える
3
 string mapName = filePath;

これはかなり基本的に怪しいように見えます。filePath文字列は、常にファイルの絶対パス名を参照する必要あります。「c:\foo\bar\baz.bin」のように、「baz.bin」のような相対ファイル名ではありません。"Global\myfile" のような名前はパス名ではなく、メモリ マップ名です。これは、別のプロセスがマッピングを開くために使用するものであり、マップのバッキング ストアとして機能する実際のファイルを認識したり気にしたりしません。

これが失敗する非常に一般的な方法は、プログラムの作業ディレクトリ (Environment.CurrentDirectory) が設定されている場所に設定されていない場合です。CreateFromFile() 呼び出しが突然失敗することを除いて、気付かないうちに変更するコツがあります。

そのため、アプリに固有の一意のマップ名を選択して、問題を解決してください。そして、フルパス名で選択したファイル。通常、UAC 昇格なしで書き込みアクセス権を持つフォルダーである AppData に格納されます。

于 2013-02-14T19:29:41.593 に答える
2

ロジックをひっくり返してはどうですか?MMF の作成は 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;
}
于 2013-02-14T19:09:17.290 に答える
1

私は質問の著者であり、ばかです。MMFは想定どおりに機能します。失敗は、MMF を作成する 2 番目の方法 (かなり前に私が作成したもの) が原因でしたが、別の MMF 命名規則を適用しました。問題のコードは (正しく) 失敗しました。これは、MMF の一部が間違った名前で 2 番目の方法で作成されたためです。

後の読者に役立つ可能性のある貴重な回答がまだ投稿されているため、質問を削除しません。時間を割いて回答してくださった皆様、どうもありがとうございました。

于 2013-02-15T10:43:21.303 に答える