12,925
回編集
(→排他処理) |
|||
376行目: | 376行目: | ||
**: 読み取りと書き込みの頻度のバランス | **: 読み取りと書き込みの頻度のバランス | ||
<br> | <br> | ||
==== | ==== lockキーワード ==== | ||
lockキーワードのメリットと特徴を以下に示す。<br> | lockキーワードのメリットと特徴を以下に示す。<br> | ||
* 使いやすさ | * 使いやすさ | ||
490行目: | 490行目: | ||
{ | { | ||
throw new IOException($"ファイルの書き込み中にエラーが発生: {ex.Message}", ex); | throw new IOException($"ファイルの書き込み中にエラーが発生: {ex.Message}", ex); | ||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
==== ReaderWriterLockSlimキーワード ==== | |||
以下の例では、複数のスレッドが頻繁に読み取りを行い、時々書き込みを行うシナリオを想定している。<br> | |||
具体的には、共有されるデータ構造 (この場合は設定ファイル) に対して、多数の読み取り操作と少数の更新操作が行われる状況である。<br> | |||
<br> | |||
ReaderWriterLockSlimキーワードのメリットを以下に示す。<br> | |||
* 複数の読み取り操作が同時に行われることを許可する。 | |||
* 書き込み操作が行われる場合には、他の全ての操作 (読み取りと書き込み) をブロックする。 | |||
<br> | |||
ReaderWriterLockSlimキーワードが適している理由を以下に示す。<br> | |||
* 書き込み時の整合性保護 | |||
*: 稀に発生する書き込み操作時には、全ての操作をブロックしてデータトレースを防ぐ。 | |||
* パフォーマンス | |||
*: lockキーワードを使用した場合、全ての読み取り操作が直列化されてしまうが、 | |||
*: ReaderWriterLockSlimキーワードを使用することにより、読み取り操作のスループットが大幅に向上する。 | |||
<br> | |||
読み取りが多く書き込みが少ない場合は、<code>ReaderWriterLockSlim</code>キーワードの使用が適切である。<br> | |||
<br> | |||
ただし、読み取りと書き込みの頻度が同程度の場合、あるいは、ロックの保持時間が非常に短い場合は、lockキーワードの方がオーバーヘッドが少なく、パフォーマンスが良い可能性がある。<br> | |||
実務では、ベンチマークを取ることを推奨する。<br> | |||
<br> | |||
<syntaxhighlight lang="c#"> | |||
using System; | |||
using System.IO; | |||
using System.Collections.Generic; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
// ReaderWriterLockSlim を使用して、設定の読み取りと書き込みを同期するクラス | |||
class ConfigManager | |||
{ | |||
private static readonly ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim(); | |||
private static Dictionary<string, string> config = new Dictionary<string, string>(); // 設定を保持する (ファイルにも保存) | |||
private const string configFile = "config.txt"; | |||
// 読み取りロックを使用 | |||
public static string GetConfig(string key) | |||
{ | |||
rwLock.EnterReadLock(); | |||
try | |||
{ | |||
return config.TryGetValue(key, out var value) ? value : null; | |||
} | |||
finally | |||
{ | |||
rwLock.ExitReadLock(); | |||
} | |||
} | |||
// 書き込みロックを使用 | |||
public static void SetConfig(string key, string value) | |||
{ | |||
rwLock.EnterWriteLock(); | |||
try | |||
{ | |||
config[key] = value; | |||
SaveConfig(); | |||
} | |||
finally | |||
{ | |||
rwLock.ExitWriteLock(); | |||
} | |||
} | |||
private static void SaveConfig() | |||
{ | |||
using (StreamWriter writer = new StreamWriter(configFile, false)) | |||
{ | |||
foreach (var kvp in config) | |||
{ | |||
writer.WriteLine($"{kvp.Key}={kvp.Value}"); | |||
} | |||
} | |||
} | |||
public static void LoadConfig() | |||
{ | |||
rwLock.EnterWriteLock(); | |||
try | |||
{ | |||
config.Clear(); | |||
if (File.Exists(configFile)) | |||
{ | |||
foreach (var line in File.ReadAllLines(configFile)) | |||
{ | |||
var parts = line.Split('='); | |||
if (parts.Length == 2) | |||
{ | |||
config[parts[0]] = parts[1]; | |||
} | |||
} | |||
} | |||
} | |||
finally | |||
{ | |||
rwLock.ExitWriteLock(); | |||
} | |||
} | |||
} | |||
class Program | |||
{ | |||
static async Task Main() | |||
{ | |||
ConfigManager.LoadConfig(); | |||
var readTasks = new List<Task>(); | |||
var writeTasks = new List<Task>(); | |||
// 多数の読み取りタスクを作成 | |||
for (int i = 0; i < 100; i++) | |||
{ | |||
readTasks.Add(Task.Run(() => ReadConfig())); | |||
} | |||
// 少数の書き込みタスクを作成 | |||
for (int i = 0; i < 5; i++) | |||
{ | |||
writeTasks.Add(Task.Run(() => WriteConfig(i))); | |||
} | |||
await Task.WhenAll(readTasks.Concat(writeTasks)); | |||
Console.WriteLine("全てのタスクが完了"); | |||
} | |||
static void ReadConfig() | |||
{ | |||
for (int i = 0; i < 1000; i++) | |||
{ | |||
var value = ConfigManager.GetConfig("TestKey"); | |||
Console.WriteLine($"Read: TestKey = {value}"); | |||
Thread.Sleep(10); // 読み取り操作の間隔 | |||
} | |||
} | |||
static void WriteConfig(int writerIndex) | |||
{ | |||
for (int i = 0; i < 10; i++) | |||
{ | |||
ConfigManager.SetConfig("TestKey", $"Value{writerIndex}-{i}"); | |||
Console.WriteLine($"Write: TestKey = Value{writerIndex}-{i}"); | |||
Thread.Sleep(500); // 書き込み操作の間隔 | |||
} | } | ||
} | } |