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

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

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

TPL で メインスレッドの優先順位をあげる方法

[トピック内 33 記事 (1 - 20 表示)]  << 0 | 1 >>

■97765 / inTopicNo.1)  TPL で メインスレッドの優先順位をあげる方法
  
□投稿者/ メタルスライム (13回)-(2021/07/11(Sun) 20:30:59)

分類:[.NET 全般] 

2021/07/11(Sun) 20:37:45 編集(投稿者)

魔界の仮面弁士様及びWEBSURFER様のご指導により
マルチスレッドでのUI更新については完全に解決しました。


魔界の仮面弁士様のレスは全てのトピックを網羅したご指導で
勉強するほど意味がわかり、
マルチスレッドに関する下記のことがすごく勉強になりました

●マルチスレッドの非同期、同期、並列、平行 のトピック
●共有変数の取扱の方法や注意点
●UI更新のたくさんの方法や注意点


ここで解決しなかった問題を質問させていただきます

TPL で表現されている
task や parallel を使用する場合

UIスレッドがフリーズしてしまい、
キャンセルボタンを反応させることができません。

キャンセルボタンをリアルタイムで反応させる方法は
あるのでしょうか


メインスレッドの優先順位を上げれば実現しそうな気がしたので
タイトルにさせていただいております


メインスレッドに対するキューの割り込み
というような操作ができればという妄想を考えています



ご指導いただけましたら幸いです




引用返信 編集キー/
■97766 / inTopicNo.2)  Re[1]: TPL で メインスレッドの優先順位をあげる方法
□投稿者/ WebSurfer (2287回)-(2021/07/11(Sun) 21:07:52)
No97765 (メタルスライム さん) に返信

> UIスレッドがフリーズしてしまい、
> キャンセルボタンを反応させることができません。

フリーズさせないようにする方法は前のスレッドで紹介したはずですけど????
引用返信 編集キー/
■97769 / inTopicNo.3)  Re[2]: TPL で メインスレッドの優先順位をあげる方法
□投稿者/ メタルスライム (14回)-(2021/07/12(Mon) 00:49:36)
2021/07/12(Mon) 02:01:39 編集(投稿者)
2021/07/12(Mon) 00:55:10 編集(投稿者)

WebSurfer さま

ご回答ありがとうございます
フリーズという表現が悪かったかもしれません
UIは更新されるが、ボタンは受付ない です。

ご紹介いただいたコードの内容は下記で再現できておりますか


この場合、私の環境では
UIは更新されるが、ボタンは受付ない です。



Private Sub Button1_Click_3(sender As Object, e As EventArgs) Handles Button3.Click
HIDOUKI()
End Sub

Private Async Sub HIDOUKI()
Await Task.Run(Function() Parallel.[For](0, 500,
Sub(n)

Me.BeginInvoke(Sub()
Me.Text = ”実行中_” & n
Me.Refresh()
System.Threading.Thread.Sleep(100)
End Sub)

System.Threading.Thread.Sleep(1000)

End Sub))

MsgBox("完了")

End Sub




Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
WhenAllProgress_Click()
End Sub
Private Async Sub WhenAllProgress_Click()
Dim taskList = New List(Of Task)()
For i As Integer = 0 To 500 - 1
Dim n As Integer = i
taskList.Add(Task.Run(Sub()
Me.BeginInvoke(Sub()
Me.Text = ”実行中_” & n
Me.Refresh()
System.Threading.Thread.Sleep(100)
End Sub)

System.Threading.Thread.Sleep(1000)
End Sub))
Next

Await Task.WhenAll(taskList.ToArray())

MsgBox("完了")

End Sub

引用返信 編集キー/
■97770 / inTopicNo.4)  Re[3]: TPL で メインスレッドの優先順位をあげる方法
□投稿者/ WebSurfer (2288回)-(2021/07/12(Mon) 07:16:05)
No97769 (メタルスライム さん) に返信

何をしたいのか、何をしているのか分かりません。

「キャンセルボタン」って何ですか? そもそもキャンセルってどういう意味ですか?
引用返信 編集キー/
■97771 / inTopicNo.5)  Re[3]: TPL で メインスレッドの優先順位をあげる方法
□投稿者/ Azulean (1196回)-(2021/07/12(Mon) 07:24:39)
2021/07/12(Mon) 07:25:15 編集(投稿者)

No97769 (メタルスライム さん) に返信
> Await Task.Run(Function() Parallel.[For](0, 500,
> Sub(n)
>
> Me.BeginInvoke(Sub()
> Me.Text = ”実行中_” & n
> Me.Refresh()
> System.Threading.Thread.Sleep(100)
> End Sub)
>
> System.Threading.Thread.Sleep(1000)
>
> End Sub))

Refresh は無理矢理その場で画面の更新処理を行うメソッドです。
これを 100ms 間隔×複数スレッド で BeginInvoke しているコードだと、たとえば、25ms 単位で描画し直すループと等しいです。

マウスイベント、キーボードイベントは、他の処理よりも優先度が低いのは「Windows の仕様」なので変えられません。
メインスレッドの負荷を下げてください。

具体的には Refresh を連打しないでください。
そのうち更新されるはずですので。


考え方の違いなのだと思いますが、処理経過をすべて見せる必要はないはずです。
描画が追いつかないならスキップ(値が飛ぶ)のは普通です。
値を飛ばさないようにしてしまうと、今回のようにマウス操作を受け付けなかったり、必ず値を表示する分、完了が遅くなるなど、弊害が発生します。
引用返信 編集キー/
■97772 / inTopicNo.6)  Re[3]: TPL で メインスレッドの優先順位をあげる方法
□投稿者/ WebSurfer (2289回)-(2021/07/12(Mon) 07:58:09)
No97769 (メタルスライム さん) に返信

ひとつ言い忘れました。

コードをアップする際はインデントされるよう「図表モード」を使ってください。
引用返信 編集キー/
■97774 / inTopicNo.7)  Re[4]: TPL で メインスレッドの優先順位をあげる方法
□投稿者/ メタルスライム (15回)-(2021/07/12(Mon) 23:25:15)
2021/07/13(Tue) 07:24:38 編集(投稿者)
Azulean 様

サンプルコードはRefreshをしなかったとしても
ある程度負荷のかかるコードをメインスレッドで実施しなければならない場合の意味をこめて
 System.Threading.Thread.Sleep(100)
を入れています


下記のコードを非同期にして、キャンセルボタン等を受付することは無理なのでしょうか?




Private Sub Button1_Click_3(sender As Object, e As EventArgs) Handles Button3.Click
HIDOUKI()
End Sub

Private Async Sub HIDOUKI()
Await Task.Run(Function() Parallel.[For](0, 500,
Sub(n)

Me.BeginInvoke(Sub()
Me.Text = ”実行中_” & n
Me.Refresh()
System.Threading.Thread.Sleep(100)
End Sub)

System.Threading.Thread.Sleep(1000)

End Sub))

MsgBox("完了")

End Sub




Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
WhenAllProgress_Click()
End Sub
Private Async Sub WhenAllProgress_Click()
Dim taskList = New List(Of Task)()
For i As Integer = 0 To 500 - 1
Dim n As Integer = i
taskList.Add(Task.Run(Sub()
Me.BeginInvoke(Sub()
Me.Text = ”実行中_” & n
Me.Refresh()
System.Threading.Thread.Sleep(100)
End Sub)

System.Threading.Thread.Sleep(1000)
End Sub))
Next

Await Task.WhenAll(taskList.ToArray())

MsgBox("完了")

End Sub


websurferさま
すみません図形モードで張れば
自動的にインデントされるかと思いましたが
そんなわけなかったです

引用返信 編集キー/
■97775 / inTopicNo.8)  Re[5]: TPL で メインスレッドの優先順位をあげる方法
□投稿者/ 魔界の仮面弁士 (3151回)-(2021/07/13(Tue) 00:12:00)
No97774 (メタルスライム さん) に返信
> 下記のコードを非同期にして、キャンセルボタン等を受付することは無理なのでしょうか?
車は急には止まれません。

UI スレッドから中止命令を投げるためには、
ワーカースレッド側に中止を受け付ける仕組みが必要です。

そこでワーカースレッドは、「信号機」の状態を適宜確認し、
信号が赤だったら停止、青なら続行するような実装にしておきます。
そして、UI スレッドは「信号機」を赤信号にすることで、
ワーカースレッドに対して中止命令を発行します。

Task ベースの場合は、CancellationTokenSource を使います。
BackgroundWorker なら、CancelAsync / CancellationPending です。

https://docs.microsoft.com/ja-jp/dotnet/standard/parallel-programming/how-to-cancel-a-task-and-its-children
https://www.atmarkit.co.jp/fdotnet/dotnettips/437bgwcancel/bgwcancel.html
引用返信 編集キー/
■97776 / inTopicNo.9)  Re[6]: TPL で メインスレッドの優先順位をあげる方法
□投稿者/ メタルスライム (16回)-(2021/07/13(Tue) 00:25:04)
魔界の仮面弁士様

ご指導の通りの方法で
キャンセルするフラグを立ててタスクを終了させたいのですが


非同期内での繰り返し作業中
Me.BeginInvoke でメインスレッドにUI更新をさせていると

メインスレッドにUI更新のキューが溜まりすぎて(なのかわかりませんが)
メインスレッドでボタン操作を受け付けることができない状態です


UI更新作業と
キャンセルボタンを受け付けることを
同時に実現する方法はあるのでしょうか



引用返信 編集キー/
■97777 / inTopicNo.10)  Re[5]: TPL で メインスレッドの優先順位をあげる方法
□投稿者/ Azulean (1197回)-(2021/07/13(Tue) 07:02:54)
2021/07/13(Tue) 07:28:50 編集(投稿者)

No97774 (メタルスライム さん) に返信
> Azulean 様
>
> サンプルコードはRefreshをしなかったとしても
> ある程度負荷のかかるコードをメインスレッドで実施しなければならない場合の意味をこめて
> System.Threading.Thread.Sleep(100)
> を入れています
「ある程度負荷のかかるコードをメインスレッドで実施しなければならない」なら、メインスレッドはボタンなどの応答の余裕はないことになりますので、「不可能」です。

「ある程度負荷のかかるコードをメインスレッドで実施しなければならない場合」は絶対に回避してください。


> 下記のコードを非同期にして、キャンセルボタン等を受付することは無理なのでしょうか?
無理です。
理由としては「非同期」になっていないからです。

メインスレッドを「描画+100ms 停止(何も受け付けない)」を繰り返すものを、Parallel で大量に送りつけているので、ボタン応答する余裕がメインスレッドにありません。

メインスレッドは 1 つしかありませんので、メインスレッドにさせたい仕事(処理)は必ず「同時に 1 つしか実行できない」となります。
これは、Parallel で組んでいるあなたのコードから見るとある意味「同期」に見えるかもしれませんね。

ボタンクリックなどの UI 反応も「メインスレッド」ですので、「メインスレッド」を「処理で使い切っている」なら、ボタンの応答はできません。


「優先度」の問題ではありません。

> Me.BeginInvoke(Sub()
> Me.Text = ”実行中_” & n
> Me.Refresh()
> System.Threading.Thread.Sleep(100)
> End Sub)

Thread.Sleep はサンプルだとしても、メインスレッドでは最小限の処理しかさせず、非常に低負荷であることを保つ必要があります。
それができないなら、ボタンの応答はできないどころか、Windows によって白くさせられる(応答がありません)恐れがあります。
引用返信 編集キー/
■97778 / inTopicNo.11)  Re[3]: TPL で メインスレッドの優先順位をあげる方法
□投稿者/ kiku (240回)-(2021/07/13(Tue) 09:12:38)
2021/07/13(Tue) 09:21:24 編集(投稿者)
No97769 (メタルスライム さん) に返信
> 2021/07/12(Mon) 02:01:39 編集(投稿者)
> 2021/07/12(Mon) 00:55:10 編集(投稿者)
> 
> Me.BeginInvoke(Sub()
>    Me.Text = ”実行中_” & n
>    Me.Refresh()
>    System.Threading.Thread.Sleep(100)
> End Sub)
> 
> System.Threading.Thread.Sleep(1000)

Azuleanさんがおっしゃっている通りだと思います。
上記のinvoke内の処理はUIスレッドで行われます。
この処理負荷を下げる必要があります。
Sleep(100)の処理内容が画面表示に関連がないのであるならば
invokeの外にだすべきです。
関連があるならば、表示する結果を計算する処理をinvokeの外に出し、
結果を表示する機能をinvoke内で実施することで処理を減らせると思います。
なので下記のようにした方が良さそうです。

System.Threading.Thread.Sleep(100)
Me.BeginInvoke(Sub()
   Me.Text = ”実行中_” & n
   Me.Refresh()
   Sleep(100)で計算された結果をここで表示
End Sub)
System.Threading.Thread.Sleep(1000)

また、上記で改善されなければ、
まだ負荷が高いということになります。
Azuleanさんも指摘されていますが、
例えば、25ms間隔で表示されている場合、
そんなに早く同じコントロールのTextプロパティに設定し表示しても
人間の目では追うことはできません(認識できない)。
なので、表示を間引くようなことをすれば実害なく表示の負荷を下げることができると思います。

例えばですが、
invoke内で画面に表示していますが、
この処理の表示したい内容だけを表示用スタック(新規に作成する)に登録するだけにし、
invokeを使わないようにします。
新たに表示用のスレッドまたは、UIタイマーでも良いと思いますが、
例えばUIタイマーで1秒毎に表示用スタック内の情報を表示します。
表示用スタックを1つのみ取り出し、スタック内の情報をクリアする。
取り出した1つのみを画面表示するというような仕組みを入れれば
1秒毎に最新の1つのみ画面表示するということが実現でき、
かなりの処理低減につながると思います。

参考になればよいですが、、、

引用返信 編集キー/
■97779 / inTopicNo.12)  Re[5]: TPL で メインスレッドの優先順位をあげる方法
□投稿者/ WebSurfer (2290回)-(2021/07/13(Tue) 09:58:22)
No97774 (メタルスライム さん) に返信

> すみません図形モードで張れば
> 自動的にインデントされるかと思いましたが
> そんなわけなかったです

元々インデントされてないコードを張ったのでしょうが、それで図表モードにして自動的に
インデントされるわけないじゃないですか。

話が通じない人? それとも単にテキトーにやっただけなのかな?


No97769 (メタルスライム さん) に返信

> ご紹介いただいたコードの内容は下記で再現できておりますか

そんなコードを紹介した覚えはないです。

紹介したのは、前のスレッド http://bbs.wankuma.com/index.cgi?mode=al2&namber=97577
No97585 のコードと、それの TPL を使った方をフリーズさせないようにした No97608 のコ
ードです。

紹介したコードはフリーズしません。

それにあなたは、

Me.Refresh()
System.Threading.Thread.Sleep(100)

という UI スレッドで動かすコードを追加して、わざわざフリーズさせてたということです。


No97765 (メタルスライム さん) に返信

> キャンセルボタンをリアルタイムで反応させる方法は
> あるのでしょうか

前にも聞きましたが・・・

「キャンセルボタン」って何ですか? あなたのコードのどこにもそんなものはなさそうです
けど。

そもそもあなたが言うキャンセルってどういう意味ですか?

私と Q&A をする気があるなら、私が聞いたことには答えてもらえませんか。

引用返信 編集キー/
■97780 / inTopicNo.13)  Re[5]: TPL で メインスレッドの優先順位をあげる方法
□投稿者/ WebSurfer (2291回)-(2021/07/13(Tue) 10:05:15)
No97774 (メタルスライム さん) に返信
 
> websurferさま
> すみません図形モードで張れば
> 自動的にインデントされるかと思いましたが
> そんなわけなかったです

あなたのコードを Visual Studio のエディタ画面にコピペしてそれを図表モードでここに貼りなおすと
以下のようになります。見てもらわないと話が始まらないのだから、見てもらえる努力をしませんか?

    Private Sub Button1_Click_3(sender As Object, e As EventArgs) Handles Button3.Click
        HIDOUKI()
    End Sub

    Private Async Sub HIDOUKI()
        Await Task.Run(Function() Parallel.[For](0, 500,
        Sub(n)

            Me.BeginInvoke(Sub()
                               Me.Text = ”実行中_” & n
                               Me.Refresh()
                               System.Threading.Thread.Sleep(100)
                           End Sub)

            System.Threading.Thread.Sleep(1000)

        End Sub))

        MsgBox("完了")

    End Sub




    Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
        WhenAllProgress_Click()
    End Sub
    Private Async Sub WhenAllProgress_Click()
        Dim taskList = New List(Of Task)()
        For i As Integer = 0 To 500 - 1
            Dim n As Integer = i
            taskList.Add(Task.Run(Sub()
                                      Me.BeginInvoke(Sub()
                                                         Me.Text = ”実行中_” & n
                                                         Me.Refresh()
                                                         System.Threading.Thread.Sleep(100)
                                                     End Sub)

                                      System.Threading.Thread.Sleep(1000)
                                  End Sub))
        Next

        Await Task.WhenAll(taskList.ToArray())

        MsgBox("完了")

    End Sub

引用返信 編集キー/
■97791 / inTopicNo.14)  Re[4]: TPL で メインスレッドの優先順位をあげる方法
□投稿者/ メタルスライム (17回)-(2021/07/13(Tue) 22:46:30)
WebSurfer さま

コードの貼付けかたについてすみませんでした。今後は気をつけさせていただきます
また、キャンセルボタンは、パラレルのループ処理のキャンセルはよくある話題かと思いますので、説明を省略したことも今後気をつけます


寸分違わないコードではありませんが、ご紹介いただいたコードから派生したコードですので
できましたら、記載のコードの内容に対して、コメントをいただけましたら幸いです



kiku さま

なるべくUIスレッドの負荷を下げる努力をする、ということですね
できることはやってみたいと思います
ありがとうございます



Azulean さま

ご記載の内容理解できました。
キューの割り込みというのは、無理な話ということですね。

とにかく負荷を下げる努力をしてみます



引用返信 編集キー/
■97792 / inTopicNo.15)  Re[5]: TPL で メインスレッドの優先順位をあげる方法
□投稿者/ WebSurfer (2295回)-(2021/07/13(Tue) 23:05:56)
No97791 (メタルスライム さん) に返信

> 寸分違わないコードではありませんが、ご紹介いただいたコードから派生したコードですので

派生なんて言わないでほしい。改悪です。そう言ったのにそれを認識してないのが不思議。

そんなので、

> ご紹介いただいたコードの内容は下記で再現できておりますか

とか言わないでほしい。失礼すぎる。質問にも答えてないし・・・
引用返信 編集キー/
■97793 / inTopicNo.16)  Re[6]: TPL で メインスレッドの優先順位をあげる方法
□投稿者/ メタルスライム (19回)-(2021/07/13(Tue) 23:29:02)
WebSurfer さま


改悪とはおっしゃいますが、ルーツは同じなわけです

コードを御覧いただけましたら
みなさま同様に、私の実現したい行為は掌握できるかと思うので

改善の方法をご指導いただきたい状況です

ご検討いただけましたら幸いです
引用返信 編集キー/
■97794 / inTopicNo.17)  Re[7]: TPL で メインスレッドの優先順位をあげる方法
□投稿者/ WebSurfer (2296回)-(2021/07/14(Wed) 10:57:23)
No97793 (メタルスライム さん) に返信

> 改悪とはおっしゃいますが、ルーツは同じなわけです

私が紹介したコードをいじって作ったあなたのコードはフリーズするのですよね。それ
を改悪と言わずして何と言うのでしょう? しかも致命的ですし。

私が紹介したものとあなたが改悪したものと一緒にしないでください。致命的な改悪をし
た時点で私の紹介したコードはルーツなどではないのですよ。

> コードを御覧いただけましたら

インデントされてないコードは見る気がしません。見ろと言うなら見てもらうための努力
をするべきです。

> みなさま同様に、私の実現したい行為は掌握できるかと思うので

何故その「私の実現したい行為」をここに書いてあること以外は知り得ない第三者が読んで
分かるように文書化できないのですか? それはここのような文法でやり取りする Q&A サイト
では必須ですよ。

回答者・閲覧者の想像に期待している? それは間違ってます。

引用返信 編集キー/
■97805 / inTopicNo.18)  Re[8]: TPL で メインスレッドの優先順位をあげる方法
□投稿者/ メタルスライム (20回)-(2021/07/14(Wed) 19:53:26)
2021/07/14(Wed) 19:54:31 編集(投稿者)

WebSurfer さま

伝える努力はしているつもりなんですが
足りないかもしれません。すみません。

おっしゃる通りの改悪ですが
負荷をかけると期待した動作をしないという状況があったので
その改善に取り組んでいるところです

私の至らないところはあるあかと存じますが、ご容赦いただけましたら幸いです

WebSurfer さま他みなさまのアドバイスで全ての問題が解決寸前のところまで来ていると思っています

感謝しかありませんので、最後は完動するコードの投稿ができたら
と思いますので
100点ではないかと思いますが、できの悪い部下の粗相と思って大目に見てていただければ幸いです



引用返信 編集キー/
■97811 / inTopicNo.19)  Re[9]: TPL で メインスレッドの優先順位をあげる方法
□投稿者/ メタルスライム (22回)-(2021/07/17(Sat) 07:16:19)
2021/07/17(Sat) 12:42:49 編集(投稿者)

解決方法を考えたので
その概念について、皆様のご意見を聞かせてほしいです


【解決したい問題】
●マルチスレッド、tplなどのプログラムにキャンセルボタンを実装したい
●UIスレッドに負荷をかけるとキャンセルボタンが反応しない

【解決方法】
別のプロセスに頼るのが良いと考えました、具体的には下記の手順です



@元プロセスから新プロセス呼び出し、キャンセル用のダイアログを生成
topmostで
A元プロセスでtplで時間のかかる繰り返し作業開始
B元プロセスで進行状況を表示
もちろん画面はフリーズしない状態

【キャンセルの場合】
C新プロセスでキャンセル目印の外部ファイルを出力 → 新プロセス終了
D元プロセスからファイルの存在をチェック、キャンセル目印のファイルがあればキャンセル処理

【正常終了の場合】
C元プロセスが動作が完了したら終了目印のファイルを生成
D新プロセスで終了目印のファイルを見つけたら新プロセス終了




引用返信 編集キー/
■97812 / inTopicNo.20)  Re[10]: TPL で メインスレッドの優先順位をあげる方法
 
□投稿者/ Azulean (1198回)-(2021/07/17(Sat) 08:46:25)
2021/07/17(Sat) 08:56:28 編集(投稿者)

No97811 (メタルスライム さん) に返信
> 【解決したい問題】
> ●マルチスレッド、tplなどのプログラムにキャンセルボタンを実装したい
> ●UIスレッドに負荷をかけるとキャンセルボタンが反応しない

前も書きましたが、UI スレッドに負荷をかけるという前提をなくしましょうよ。
UI スレッドに負荷がかかっていると、「Windows から応答なし」とみなされるリスクがありますので、使う人が強制終了するかもしれませんよ。

Microsoft としても、Async-Await パターンを用意するなど、処理をバックグラウンドに移し、UI 応答性を上げる思想を前面に出していますので。

> 【解決方法】
> 別のプロセスに頼るのが良いと考えました、具体的には下記の手順です

仮に別プロセスに頼るなら「処理する側を別プロセス」にすることが多いと思うので、個人的に言わせれば、「普通やらないような、変なことをしている」という認識です。


-----
本当にどうしようもない場合は、非推奨の UI スレッドをもう 1 つ作る方が先だとは思う。
ただ、UI の負荷が避けられないという問題に焦点を当てず、あるいはどうしようもないと思い込んでいる現状を見直さない状況で、別プロセスや 2 つ目の UI スレッドなどのトリッキーなやり方に進むことは賛同しかねます。
引用返信 編集キー/

このトピックをツリーで一括表示

次の20件>
トピック内ページ移動 / << 0 | 1 >>

管理者用

- Child Tree -