C# と VB.NET の質問掲示板

わんくま同盟

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

ツリー一括表示

バックグラウンドプロセスを待機させるとInvokeでフリーズ /造形 (19/11/01(Fri) 14:49) #92834
Re[1]: バックグラウンドプロセスを待機させるとInvokeでフリ.. /Hongliang (19/11/01(Fri) 15:02) #92835
  └ Re[2]: バックグラウンドプロセスを待機させるとInvokeでフリ.. /造形 (19/11/01(Fri) 15:18) #92836
    ├ Re[3]: バックグラウンドプロセスを待機させるとInvokeでフリ.. /造形 (19/11/01(Fri) 15:27) #92837
    └ Re[3]: バックグラウンドプロセスを待機させるとInvokeでフリ.. /魔界の仮面弁士 (19/11/01(Fri) 15:36) #92841
      └ Re[4]: バックグラウンドプロセスを待機させるとInvokeでフリ.. /造形 (19/11/01(Fri) 15:35) #92840
        └ Re[5]: バックグラウンドプロセスを待機させるとInvokeでフリ.. /造形 (19/11/01(Fri) 15:56) #92842
          └ Re[6]: バックグラウンドプロセスを待機させるとInvokeでフリ.. /造形 (19/11/02(Sat) 12:44) #92848
            └ Re[7]: バックグラウンドプロセスを待機させるとInvokeでフリ.. /Azulean (19/11/02(Sat) 18:20) #92849
              └ Re[8]: バックグラウンドプロセスを待機させるとInvokeでフリ.. /造形 (19/11/03(Sun) 10:55) #92858
                └ Re[9]: バックグラウンドプロセスを待機させるとInvokeでフリ.. /Azulean (19/11/03(Sun) 17:15) #92866
                  └ Re[10]: バックグラウンドプロセスを待機させるとInvokeでフリ.. /造形 (19/11/03(Sun) 18:05) #92867
                    └ Re[11]: バックグラウンドプロセスを待機させるとInvokeでフリ.. /Azulean (19/11/03(Sun) 18:47) #92868
                      └ Re[12]: バックグラウンドプロセスを待機させるとInvokeでフリ.. /造形 (19/11/05(Tue) 14:02) #92885 解決済み


親記事 / ▼[ 92835 ]
■92834 / 親階層)  バックグラウンドプロセスを待機させるとInvokeでフリーズ
□投稿者/ 造形 (1回)-(2019/11/01(Fri) 14:49:46)

分類:[.NET 全般] 

DataGridViewにファイルリストがあり、
そのリストをクリックするとそのファイルを読み込み処理するプログラムを書いています。

普通に書くと、


    Private Sub DataGridView1_MouseClick(sender As Object, e As MouseEventArgs) Handles DataGridView1.MouseClick

                    Call FileImport()


    End Sub

というようになります。

ただ、GUIスレッドで読み込み処理を行うと
GUIが一時的にフリーズしてしまうため、
バックグラウンド処理で行いたいと考えています

そのため、以下のように改良しました。


    Private Sub DataGridView1_MouseClick(sender As Object, e As MouseEventArgs) Handles DataGridView1.MouseClick

            Dim TTT As Task = Task.Factory.StartNew(
                Sub()

                    Call FileImport()

                End Sub)


            TTT.Wait()

    End Sub


最後にWaitしているのは、読み込みが完了する前に
再度DataGridViewをクリックすると処理が二重に行われるためです。

この方法の問題点は、


                    Call FileImport()

の中でInvokeしようとすると、そこでプログラムがフリーズしてしまうことです。
TTT.Wait()でGUIスレッドは待機しているために、Invokeを受け付けないようです。


WaitしていてもInvokeを優先的に実行するにはどのようにしたら良いですか?

[ □ Tree ] 返信 編集キー/

▲[ 92834 ] / ▼[ 92836 ]
■92835 / 1階層)  Re[1]: バックグラウンドプロセスを待機させるとInvokeでフリーズ
□投稿者/ Hongliang (912回)-(2019/11/01(Fri) 15:02:17)
Taskを使うのでしたら、Async/Awaitを使うのはどうでしょうか?

Private Async Sub DataGridView1_MouseClick(略) Handles DataGridView1.MouseClick
    ' 操作できたら困るUI要素を一旦Enabled = Falseにする
    DataGridView1.Enabled = False

    Await Task.Run(略)

    DataGridView1.Enabled = True
End Sub

[ 親 92834 / □ Tree ] 返信 編集キー/

▲[ 92835 ] / ▼[ 92837 ] ▼[ 92841 ]
■92836 / 2階層)  Re[2]: バックグラウンドプロセスを待機させるとInvokeでフリーズ
□投稿者/ 造形 (2回)-(2019/11/01(Fri) 15:18:40)
https://webbibouroku.com/Blog/Article/async-await-task

Dim result As Integer = Await Task.Run(
Function() As Integer
For index = 0 To Integer.MaxValue - 1
Next
Return 1
End Function
)


と書くとRunはTaskのメンバーではありません
というエラーが出てしまうのですが・・・


ちなみに、コードが違うだけで


TTT.Wait()

とするのとやっていることは同じですよね?



[ 親 92834 / □ Tree ] 返信 編集キー/

▲[ 92836 ] / 返信無し
■92837 / 3階層)  Re[3]: バックグラウンドプロセスを待機させるとInvokeでフリーズ
□投稿者/ 造形 (3回)-(2019/11/01(Fri) 15:27:36)
すいません、
Awaitを使うとプログラムをいじっているうちに

重大度レベル コード 説明 プロジェクト ファイル 行 抑制状態
エラー BC31091 型 'IAsyncStateMachine' を、アセンブリまたはモジュール 'xxx.exe' からインポートできませんでした。
エラー BC31091 型 'AsyncVoidMethodBuilder' を、アセンブリまたはモジュール 'xxx.exe' からインポートできませんでした。
エラー BC35000 ランタイム ライブラリ関数 'System.Runtime.CompilerServices.IAsyncStateMachine.SetStateMachine' が定義されていないため、要求された操作を実行できません。
エラー BC35000 ランタイム ライブラリ関数 'System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext' が定義されていないため、要求された操作を実行できません。
警告 BC42356 この Async メソッドには 'Await' 演算子がないため、同期で実行されます。ブロック不可の API 呼び出しを待つには 'Await' 演算子を、バックグラウンド スレッドに CPU 主体の操作をするには 'Await Task.Run(...)' を使用することを検討してください。


というエラーが出てきて消えなくなりました。
エラーが出ている場所のコードを削除してもこのエラーは消えてくれません。
form1.designerに何か追加されたのかと思ったのですが
そういうわけではないようです。

どうすれば元の状態に戻せますか?

[ 親 92834 / □ Tree ] 返信 編集キー/

▲[ 92836 ] / 返信無し
■92841 / 3階層)  Re[3]: バックグラウンドプロセスを待機させるとInvokeでフリーズ
□投稿者/ 魔界の仮面弁士 (2453回)-(2019/11/01(Fri) 15:36:06)
No92836 (造形 さん) に返信
> と書くとRunはTaskのメンバーではありません

ターゲット フレームワークが .NET Framework 4 になっていないでしょうか?
Task.Run メソッドは、.NET Framework 4.5 以降の機能です。


※ .NET Framework 4、4.5、4.5.1 のサポートは 2016 年 1 月 12 日で終了しています。
[ 親 92834 / □ Tree ] 返信 編集キー/

▲[ 92837 ] / ▼[ 92842 ]
■92840 / 4階層)  Re[4]: バックグラウンドプロセスを待機させるとInvokeでフリーズ
□投稿者/ 造形 (4回)-(2019/11/01(Fri) 15:35:44)
エラーの問題だけは解決しました
Private Async Sub

となっているのが原因でした

[ 親 92834 / □ Tree ] 返信 編集キー/

▲[ 92840 ] / ▼[ 92848 ]
■92842 / 5階層)  Re[5]: バックグラウンドプロセスを待機させるとInvokeでフリーズ
□投稿者/ 造形 (6回)-(2019/11/01(Fri) 15:56:12)
ありがとうございます。

うまくいきました
AwaitにするとInvokeが効くようになるのですね。

これってwaitにするのとどう違うのでしょうか?

[ 親 92834 / □ Tree ] 返信 編集キー/

▲[ 92842 ] / ▼[ 92849 ]
■92848 / 6階層)  Re[6]: バックグラウンドプロセスを待機させるとInvokeでフリーズ
□投稿者/ 造形 (8回)-(2019/11/02(Sat) 12:44:52)
例えば、以下のコードに関してなのですが

    Private Async Sub NumericUpDown29_ValueChanged(sender As Object, e As EventArgs) Handles NumericUpDown29.ValueChanged

        CType(sender, NumericUpDown).Enabled = False

        Await Task.Run(Sub()

                           Call FileExport()

                       End Sub)

        CType(sender, NumericUpDown).Enabled = True

    End Sub

FileExportは一カ所ではなく複数箇所で出てくるため、
上記のようにすると、
コードが長くなってしまいます。

そのため、



    Private Sub NumericUpDown29_ValueChanged(sender As Object, e As EventArgs) Handles NumericUpDown29.ValueChanged

        CType(sender, NumericUpDown).Enabled = False

	Call FileExportAsync()

        CType(sender, NumericUpDown).Enabled = True

    End Sub



    Private Async Sub FileExportAsync()


        Await Task.Run(Sub()

                           Call FileExport()

                       End Sub)


    End Sub

のようにしたいのですが、

Await Task.Run(Sub()
が終了するまえに、

Call FileExportAsync()
から抜けて
CType(sender, NumericUpDown).Enabled = True
が実行されてしまいます。

それぞれのコードで、毎回、


        Await Task.Run(Sub()


                       End Sub)

を書く必要があるのでしょうか?

[ 親 92834 / □ Tree ] 返信 編集キー/

▲[ 92848 ] / ▼[ 92858 ]
■92849 / 7階層)  Re[7]: バックグラウンドプロセスを待機させるとInvokeでフリーズ
□投稿者/ Azulean (1092回)-(2019/11/02(Sat) 18:20:25)
Async キーワードをつけた関数を呼ぶ場合、Await キーワードを使わないと待てません。
Awaitが「待つ」という意味だと思ってください。

Async キーワードがついた関数の呼び出し元をすべてたどっていって、Await していない箇所があったら何かミスがあるという風に考えてください。


No92848 (造形 さん) に返信
> Private Sub NumericUpDown29_ValueChanged(sender As Object, e As EventArgs) Handles NumericUpDown29.ValueChanged
>
> CType(sender, NumericUpDown).Enabled = False
>
> Call FileExportAsync()

・「Call FileExportAsync」というように Await を使っていないので「終了を待たない」という意味のコードを書いています」
・FileExportAsync に Await を書くためには、Sub NumericUpDown29_ValueChanged にも Async を書く必要があるでしょう。
[ 親 92834 / □ Tree ] 返信 編集キー/

▲[ 92849 ] / ▼[ 92866 ]
■92858 / 8階層)  Re[8]: バックグラウンドプロセスを待機させるとInvokeでフリーズ
□投稿者/ 造形 (9回)-(2019/11/03(Sun) 10:55:14)
ありがとうございます。


・FileExportAsync に Await を書くためには、Sub NumericUpDown29_ValueChanged にも Async を書く必要があるでしょう。

これは既に試しているのですが、
やはり、

Call FileExportAsync()
から抜けて
CType(sender, NumericUpDown).Enabled = True
が先に実行されてしまいます。

また、

警告 BC42356 この Async メソッドには 'Await' 演算子がないため、同期で実行されます。ブロック不可の API 呼び出しを待つには 'Await' 演算子を、バックグラウンド スレッドに CPU 主体の操作をするには 'Await Task.Run(...)' を使用することを検討してください。

というエラーも警告メッセージも発生してしまうのですが。

Call Async FileExportAsync()

のようにするのかと思ったのですが
エラーが出て実行できません。

[ 親 92834 / □ Tree ] 返信 編集キー/

▲[ 92858 ] / ▼[ 92867 ]
■92866 / 9階層)  Re[9]: バックグラウンドプロセスを待機させるとInvokeでフリーズ
□投稿者/ Azulean (1095回)-(2019/11/03(Sun) 17:15:19)
No92858 (造形 さん) に返信
> ・FileExportAsync に Await を書くためには、Sub NumericUpDown29_ValueChanged にも Async を書く必要があるでしょう。
>
> これは既に試しているのですが、
> やはり、
>
> Call FileExportAsync()
> から抜けて
> CType(sender, NumericUpDown).Enabled = True
> が先に実行されてしまいます。

ちゃんと伝わらなかったようですが、大事なのは「Await」を書くことです。
「Async」は、その関数の中で「Await」を使うための前提条件に過ぎず、「Async」だけ書いても何も効果がありません。

> 警告 BC42356 この Async メソッドには 'Await' 演算子がないため、同期で実行されます。ブロック不可の API 呼び出しを待つには 'Await' 演算子を、バックグラウンド スレッドに CPU 主体の操作をするには 'Await Task.Run(...)' を使用することを検討してください。

前述したように「Async」だけ書いても効果がないので、何か間違えていませんか?とコンパイラに警告してもらっています。
なお、これは「警告」であり、「エラー」ではありません。


> Call Async FileExportAsync()
>
> のようにするのかと思ったのですが
> エラーが出て実行できません。

まず、なぜ Call をつけているのでしょうか?
現代の VB.NET でメソッドを呼び出すに当たって、Call は不要のはずです。
現代的な Await を書くためには Call を書かない…といった必要があるかもしれません。(未検証)

ところで、Call "Async" というのは、ここに書く際に書き間違えているだけですよね?
[ 親 92834 / □ Tree ] 返信 編集キー/

▲[ 92866 ] / ▼[ 92868 ]
■92867 / 10階層)  Re[10]: バックグラウンドプロセスを待機させるとInvokeでフリーズ
□投稿者/ 造形 (11回)-(2019/11/03(Sun) 18:05:03)
Await FileExportAsync
というの試してはいるのですが、


エラー BC37001 'FileExportAsync' はタスクを返さないため、Await できません。Async Function への変更を検討してください。

というエラーが発生するのですが・・・

SubではなくFunctionにしないといけないのでしょうか?


[ 親 92834 / □ Tree ] 返信 編集キー/

▲[ 92867 ] / ▼[ 92885 ]
■92868 / 11階層)  Re[11]: バックグラウンドプロセスを待機させるとInvokeでフリーズ
□投稿者/ Azulean (1096回)-(2019/11/03(Sun) 18:47:19)
2019/11/03(Sun) 18:58:50 編集(投稿者)

No92867 (造形 さん) に返信
> エラー BC37001 'FileExportAsync' はタスクを返さないため、Await できません。Async Function への変更を検討してください。
>
> というエラーが発生するのですが・・・
>
> SubではなくFunctionにしないといけないのでしょうか?

その点のコメントができていなかった&今までされていなかったかもしれませんね。
Async キーワードをつける場合、以下のいずれかが必要です。

1. イベントハンドラなど、処理の流れの始点(自分でその関数を呼んでいる箇所がまったくない)となること。
2. ほかの関数から呼ばれる場合は、戻り値を Task、または Task<T> とすること。元が Sub なら Task、元が Function なら Task<T> として T は元の戻り値型とする。
※ Task<T> と書きましたが、VB.NET だと Task(Of T) かな…。

なので、FileExportAsyc を Function にして、戻り値を Task としてください。
なお、元々が Sub だったのであれば、 Return するコードを書く必要はありません。
[ 親 92834 / □ Tree ] 返信 編集キー/

▲[ 92868 ] / 返信無し
■92885 / 12階層)  Re[12]: バックグラウンドプロセスを待機させるとInvokeでフリーズ
□投稿者/ 造形 (12回)-(2019/11/05(Tue) 14:02:12)
Functionにすると
うまくいきました

ありがとうございます。

解決済み
[ 親 92834 / □ Tree ] 返信 編集キー/


管理者用

- Child Tree -