現在、C#でプログラムを作成しています。
・あるフォルダに非同期でファイルを1000個作成するプログラムを作成する
・そのフォルダのすでに作成されているフォルダの数を取得して完了率をプログレスバーに表示する
上記のプログラムを作っているのですが実行すると、あるところでデッドロックが発生してしまいます。
デッドロックを起こすことなく、進捗率を表示することのできる方法があれば知りたいのですがどなたかご教示いただけないでしょうか。
よろしくお願いいたします。
public async void MakeCsvAsync() { await Task.Run(() => { //ファイルの作成 }); loop_progressbar = false; MessageBox.Show("完了しました。"); } private void run() { MakeCsvAsync(); loop_progressbar = true; try { System.Threading.Thread.Sleep(5000); while(loop_progressbar) { int num = Directory.GetFiles(dir, "*", SearchOption.TopDirectoryOnly).Length; System.Threading.Thread.Sleep(2000); progressBar1.Value = num / 1000; } } catch(Exception e) { MessageBox.Show(e.Message); } }
ファイルの作成自体は、1000個で約一分半ほどです。
よろしくお願いいたします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答5件
0
async/awaitの挙動を理解できていないのが原因ですね。少々ややこしいです。
MakeCsvAsync
メソッドでawait Task.Run
を実行すると、別スレッドでその中身を実行開始してすぐに一旦UIスレッドに制御を返します。そして、別スレッドでの処理終了後にUIスレッドでその続きが実行されます。
ところが、MakeCsvAsync
メソッドはrun
メソッド内から呼び出されており、run
メソッドはloop_progressbar == true
の間ループしてメソッドから抜けないのでUIスレッドを占有した状態となり、await
の続きを実行することができません。結果、loop_progressbar
がいつまでたってもfalseにならず、run
メソッド内で永久ループになってしまっています。
とりあえずの回避策としては、loop_progressbar = false;
をTask.Runの中の処理の最後に書いてみてください。
あと、念のためrunメソッドのloop_progressbar = true;
はMakeCsvAsync();
の前に書いてください。
async
メソッド内のawait
後の処理は、フォームが何もメソッドを実行していない状態の時に実行されるという点に注意してください。
追記
runメソッド内で時間のかかる処理(別スレッドの処理待ち)をするのは得策ではありません。その間、フォームに対するあらゆる操作ができなくなりますし、プログレスバーのハイライトのアニメーションが見られなくなります。
こんな感じに書いた方がシンプルで判りやすいですし、UIが固まることもありません。
C#
1private async void run() 2{ 3 // 再突入抑止のためボタン無効化(ボタン押下でrunメソッドが呼ばれると仮定) 4 button1.Enabled = false; 5 6 for(int i = 0; i < 1000; i++) 7 { 8 await Task.Run(() => 9 { 10 // ファイル1個作成 11 }); 12 progressBar1.Value = i + 1; 13 } 14 MessageBox.Show("完了しました。"); 15 16 // ボタン有効化 17 button1.Enabled = true; 18}
投稿2016/11/01 13:58
編集2016/11/02 02:23総合スコア5938
0
ベストアンサー
こんにちは。
非同期の問題については既に他の方が詳しく回答しているので、
あるところでデッドロックが発生してしまいます。
の原因とその回避方法についてだけ回答してみます。
デッドロックは以下の箇所の変更のみで回避できます(はずです)。
csharp
1public async void MakeCsvAsync() 2{ 3 await Task.Run(() => 4 { 5 //ファイルの作成 6 }).ConfigureAwait(false); // <- 7 8......
この問題の詳細については、以下の情報を参照して下さい。
http://qwerty2501.hatenablog.com/entry/2014/04/24/235849
同様の事象について、発生原因とその回避方法が簡潔に解説されています。Wait
メソッドによるスレッド専有がwhile
ループによる専有に変化しているだけです。
async/awaitは他にも細かいハマりどころが多いので、まずはこの問題の発生原因をきっちり理解しておくことをオススメします。
投稿2016/11/02 02:41
総合スコア4105
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/11/02 04:26
2016/11/02 04:53
2016/11/02 05:49
2016/11/02 06:06
2016/11/02 06:28
2016/11/02 06:44
2016/11/02 06:57 編集
2016/11/02 07:27
2016/11/02 08:11
0
こんにちは。
Task.Run()の中でコントロールを描画しているとおかしなことになりますが、そのような一般的な禁止事項はされてないと仮定して回答してみます。
書かれている処理を普通に実装した時、特にデッドロックしそうな印象はありません。
恐らくWindows APIを直接使っていればデッドロックしないと思います。
2~3年前の経験ですが、C#のファイル・アクセスは単にWindows APIへパススルーしているだけでなく、何か謎の処理をしていることがありました。その時は単にテキスト・ファイルへアペンドしているだけにも関わらずファイルが大きくなると異常に遅くなる現象でした。Windows APIを直接使うようにしてあっさり解決しました。他にも再現性のない不可解な動作も幾度か経験し、それ以来C#のファイル処理はどうも信用できません。
ですので、C#のファイル・アクセスを使わないでWindows APIを直接呼び出すことで解決する可能性はあると思います。
しかし、結構手間ですね。代わりにTask.Run()から進捗を示す数値をメイン・スレッドを送ることも考えられます。
ちょっと古いですが、時間のかかる処理の進行状況を表示するで紹介されているBackgroundWorkerは、正にこの目的のための仕組み(ProgressChanged)を用意しているようです。
投稿2016/11/01 08:07
総合スコア23272
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
今回の質問とは直接関係ない余計はお世話的なレスですみませんが、どうしても気になったもので・・・
Exception をキャッチするのは止めた方がいいです。理由は以下の記事を見てください。
NETの例外処理 Part.1
https://blogs.msdn.microsoft.com/nakama/2008/12/29/net-part-1/
.NETの例外処理 Part.2
https://blogs.msdn.microsoft.com/nakama/2009/01/02/net-part-2/
.NET 4 からは破損状態例外は catch できなくなっているそうですが、「それでも Catch (Exception e) を使用するのはよくない」ということについては以下の記事を見てください。
破損状態例外を処理する
https://msdn.microsoft.com/ja-jp/magazine/dd419661.aspx
投稿2016/11/01 08:05
退会済みユーザー
総合スコア0
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
UIコンポーネントへのアクセス(※)をメインスレッド以外から行ってはいけません。まずそういった非同期処理の基本を学ぶ必要があろうかと思います。非同期処理をしつつプログレスバーなどのUIコンポーネントを更新するやりかたについてはあちこちに考え方やサンプルがあると思いますのでそれを参考にするとよいです。やりかたは一通りではありません。Form/WPFでも若干違うと思います。
「window form 非同期処理」などで検索すると情報が得られると思います。
※: この場合はプログレスバーのプロパティーアクセスです
投稿2016/11/01 07:59
編集2016/11/01 08:05総合スコア18394
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。