SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

.NET 4.0でメモリマップドファイルを使用する

単純なメモリ操作でファイルへの読み書きを簡単に

マップドファイルからの読み取り

 事前にマップされたメモリ位置を使用するには、MemoryMappedViewAccessorクラスのメソッドを使う必要があります。例えば、ファイルマップの先頭から10バイトを読み取るには、ReadByteメソッドを次のように使います。

  ...
  MemoryMappedViewAccessor accessor =
    mmf.CreateViewAccessor();
  byte[] buffer = new byte[10];
  for (int index = 0; index < buffer.Length; index++)
  {
    buffer[index] = accessor.ReadByte(index);
  }

 Readメソッドは、特定の汎用オブジェクトの内容を埋めることも、Read<T>またはReadArray<T>でジェネリックを使って、より具体的なオブジェクトを受け取ることもできます。例えば、GUID型の(構造体として定義された)オブジェクトがある場合、次の2つのReadNNNメソッド呼び出しの結果は同じようになります。

  // method 1:
  byte[] buffer = new byte[16];
  accessor.ReadArray(0, buffer, 0, buffer.Length);
  Guid guid = new Guid(buffer);
  MessageBox.Show(guid.ToString());
  
  // method 2:
  Guid guid2 = new Guid();
  accessor.Read(0, out guid2);
  MessageBox.Show(guid2.ToString());

 どちらのReadメソッド呼び出しでも、読み取り開始位置を指定する必要があることに注意してください。このゼロから始まるオフセットは、必ずしも元のファイルに対する位置ではなく、常にマップされたビューに対する位置になります。メモリマッピングオブジェクトを作成するときに、ファイルを操作するためのメモリのウィンドウを指定する必要があります(図1)。前に示したコードのようにオフセットを指定しなければ、ビューはファイルの先頭から始まるものと見なされます。

図1 ビューのオフセットは、常にマップされたビューに対する位置になる
図1 ビューのオフセットは、常にマップされたビューに対する位置になる

 柔軟性を持たせるために、ゼロのオフセットから開始して、ファイルの長さ全体をマップすることも、中間から開始して、ファイルの一部だけをマップすることもできるようになっています。アクセサオブジェクトによる読み取りは、ビューに対するオフセットを使って行われます。つまり、元のファイルのオフセットは、ビューの開始位置にビューのオフセットを加えたものになります。

 また、メモリマッピングオブジェクトと、その元になるファイルには、オペレーティングシステムハンドルがあることにも注意してください。ですから、使い終わったオブジェクトは忘れずに廃棄することが大切です。そうしないと、ガベージコレクションが作動するまで、オブジェクトはいつまでもオープンされたままになります。try-finallyブロックまたはC#のusing文を使うことをお勧めします。

 .NETストリームオブジェクトで十分に用は足りているものの、メモリマップドファイルの利点を活用したいという人にうってつけの機能があります。MemoryMappedFileクラスには、MemoryMappedViewStreamオブジェクトを返すCreateViewStreamという便利なメソッドがあります。このオブジェクトを使うと、マップされたビューへのシーケンシャルアクセスを行うことができます。ランダムアクセスが可能なアクセサオブジェクトを使う場合に比べて、このようにシーケンシャルアクセスしかできないことが、マップされたビューのストリームを使う最大の弱点です。この制限に不満がなければ、CreateViewStreamメソッドは良い味方になります。

オブジェクト間およびプロセス間でのメモリの共有

 前のセクションでは、メモリマップドファイルを使うと、単純なメモリ操作でファイルの内容に簡単にアクセスできることを示しました。ここではこの知識を基に、アプリケーション内およびプロセス間でメモリを共有する方法について説明します。

 ファイルの内容をメモリにマッピングするときは、何よりまず、ディスク上のどのファイルのどの部分をマッピングするかを指定する必要があります。ここで留意すべきことは、同じファイルを何度でもマップできるという点です。たとえマップされる領域が同じでも、重なり合っていてもかまいません(図2)。

図2 ファイルの一部を何度でもマップできる
図2 ファイルの一部を何度でもマップできる

 この機能を利用することによって、同時実行やロックを気にせずに、複数のスレッドからファイルの内容にアクセスできるようになります。メモリブロックを読み書きする方法さえ知っていれば十分で、それには前に説明したビューアクセサクラスを使います。次の例は、ファイルの先頭を複数のアクセサオブジェクトにマップする方法を示しています。当然、ファイル名は新しいビューアクセサを作成するたびに一致していなければなりません。次のように同じメモリマッピングオブジェクトを2回使えば、これは簡単に実現できます。

  ...
  MemoryMappedViewAccessor accessor1 =
    mmf.CreateViewAccessor();
  MemoryMappedViewAccessor accessor2 =
    mmf.CreateViewAccessor();
      
  // write
  byte writeChr = Encoding.ASCII.GetBytes("Z")[0];
  accessor1.Write(0, writeChr);
  
  // read
  byte readChr = accessor2.ReadByte(0);
  string status = (readChr == writeChr) ? "Match!" : "No match!";
  MessageBox.Show(status); // match

 ビューのメモリブロックへの書き込みが完了した時点で、ファイルの内容が変更されていることに注意してください。変更されたデータをオペレーティングシステムが直ちにディスクにフラッシュしないこともありますが、通常はほぼ即座に行われます。コミット操作やフラッシュ操作を別途行う必要はありません。これがメモリマップドファイルを使う利点の1つです。

 マップされたファイルをプロセス間で共有するには、ビューに名前を付ける必要があります。名前を使うことによって、同期されたビューを複数のプロセスで開けるようになります。言うまでもなく、システムの他のオブジェクト名と重複する名前は使えません。例えば、プロセス間で文字列を送信したいとします。メモリにマップされた名前付きのビューを開き、簡単な文字列をビューに書き込むコードは次のようになります。

  MemoryMappedFile mmf = MemoryMappedFile.CreateOrOpen(
    "my-mmf-map-name", 1000);
  MemoryMappedViewAccessor accessor =
    mmf.CreateViewAccessor();
  string message = "Hello, Memory-Mapped World!";
  byte[] asciiBytes = Encoding.ASCII.GetBytes(message);
  accessor.WriteArray(0, asciiBytes, 0, asciiBytes.Length);
  MessageBox.Show("Message written.");

 このコードでは、データを入れる物理ファイルが使われていないことに注意してください。そのため、CreateOrOpenメソッドを呼び出すときに容量パラメータを指定する必要があります。上のコードでは、容量を1,000バイトに設定しています。この容量はメモリブロックのサイズを定義します。これについては後で詳しく述べます。プロセス間の情報共有の例に戻ると、次にすべきことは、別のプロセスで類似の名前のビューを使って文字列を読み出すことです。

  MemoryMappedFile mmf = MemoryMappedFile.CreateOrOpen(
    "my-mmf-map-name", 1000);
  MemoryMappedViewAccessor accessor =
    mmf.CreateViewAccessor();
  byte byteValue;
  int index = 0;
  StringBuilder message = new StringBuilder();
  do
  {
    byteValue = accessor.ReadByte(index);
    if (byteValue != 0)
    {
      char asciiChar = (char)byteValue;
      message.Append(asciiChar);
    }
    index++;
  } while (byteValue != 0);
  MessageBox.Show("Found text: \""+message+"\".");

 このコードでは、MemoryMappedFileクラスのCreateOrOpen静的メソッドを使い、メモリにマップされた同じビューを別のプロセスで開いています。次に、前と同じようにアクセサオブジェクトを作成し、ゼロ終端バイトが出現するまでデータをバイト単位で読み取っています。その後で、メッセージを処理しています。この例では画面上にメッセージが表示されます。このように、プロセス間通信(IPC)をとても簡単な方法で処理できます。

次のページ
ファイルの作成、拡張、および切り詰め

この記事は参考になりましたか?

japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Jani Jarvinen(Jani Jarvinen)

フィンランドのソフトウェア開発トレーナー兼コンサルタント。Microsoft C# MVPの受賞者で、投稿も多数。ソフトウェア開発に関する著書も3冊出版している。ITpro.fiというフィンランドのソフトウェア開発エキスパートグループのグループリーダー。ブログのアドレスはhttp://www.saunalahti.fi/janij/

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/4279 2009/09/01 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー