質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

3回答

15593閲覧

c# async/await でテキスト書き込みすると抜けがある

takagi.1994

総合スコア47

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2015/12/16 16:06

private static async void hoge(object i)
{

await Task.Run(() => { FileStream fs = new FileStream("log.txt", FileMode.Append, FileAccess.Write, FileShare.ReadWrite); StreamWriter sw = new StreamWriter(fs, System.Text.Encoding.UTF8); TextWriter tw = TextWriter.Synchronized(sw); tw.WriteLine(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss:ffff")); tw.Flush(); fs.Close(); } );

}

for (int i = 0; i < 100; i++)
{
hoge(i);
}

この場合、書き出されたファイルを見ると抜けがあるのですが、どうしたらよいのでしょうか?

よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

非同期で同時並行的に実行されるので、同じファイルへの書き込みが競合し、一度書いたところを別のスレッドが上書きしたりして抜けたようになるのだと思います。

複数スレッドからの呼び出しで安全に動作(いわゆるスレッドセーフ)させたいのであれば、排他制御するのが一般的です。C#にはlockという便利な機能があります。
async/awaitはこういうケースとは違う目的で使うので、ここでは使う必要はありません。
コード例

C#

1private static object locker = new object(); 2 3private static void hoge(object i) 4{ 5 lock(locker) // 複数スレッドから同時にhogeが呼ばれても、このブロック内は1スレッドずつ実行されます。 6 { 7 FileStream fs = new FileStream("log.txt", FileMode.Append, FileAccess.Write, FileShare.Read); 8 StreamWriter sw = new StreamWriter(fs, System.Text.Encoding.UTF8); 9 sw.WriteLine(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss:ffff")); 10 sw.Flush(); 11 fs.Close(); 12 } 13}

とりあえず、これで抜けが生じることはなくなります。排他制御をしているのでTextWriter.Synchronizedは不要です。また、同時にオープンすることはなくなるので、ファイル共有モードはFileShare.Readで良いです。
ただ、ファイルオープン/クローズは結構時間がかかる処理なので、できればStreamWriterまではあらかじめ作っておいて、ファイル書き出しメソッドではWriteLine/Flushだけを行い、終了時にクローズする、というようにした方が良いと思います。

投稿2015/12/16 22:08

catsforepaw

総合スコア5938

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takagi.1994

2015/12/17 07:39

ご丁寧な回答ありがとうございます。 アドバイスを参考に書き直してみます。
guest

0

非同期実行、マルチスレッド実行でスレッドセーフでは無いものに触る時は常にクリティカルセクションを意識しましょう

早い時点で見つかってよかったですね
見つからない時は実運用になって発覚とかままありますので..

投稿2015/12/17 03:00

編集2015/12/17 03:01
dojikko

総合スコア3939

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takagi.1994

2015/12/17 07:40

ありがとうございます。知らないことばかりで参考になりました。
guest

0

こんにちは。

FileStreamコンストラクタでロックが発生し、排他を取られている間に、
並列処理によって処理順序が保証されなくなってしまっているのでは。

ファイル書き込み処理を非同期にしたいのであればforブロックを含めて非同期化することを検討してはどうでしょうか。

投稿2015/12/16 16:53

Tak1wa

総合スコア4791

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takagi.1994

2015/12/16 17:06

回答ありがとうございます。 実際に行いたいのは、logを残すためtxtファイルに追記させていきたいのですが、 forとしたのは、実際にはあまりないのですが、複数のスレッドから大量のログが吐き出されたときにすべて残るようにしたいのですが、その想定としてforでまわしてみたのです。
Tak1wa

2015/12/17 00:13

そういう用途であれば、catsforepawさんが仰っているようにlock機構を使用してスレッドセーフな実装を行うようにしてください。
takagi.1994

2015/12/17 07:42

貴重なアドバイスありがとうございました。
Tak1wa

2015/12/17 07:49

ちなみにログ出力用途であればTraceListenerを利用すると良いです。(※スレッドセーフな実装は別途必要)。log4netなどは基本的にスレッドセーフに作られているので利用を検討するのも良いと思います。
takagi.1994

2015/12/18 15:38

ありがとうございます。 勉強してみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問