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

わんくま同盟

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

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

ツリー一括表示

時間のかかる処理の進行の表示 /焼いも (23/11/12(Sun) 15:19) #102521
Re[1]: 時間のかかる処理の進行の表示 /WebSurfer (23/11/12(Sun) 15:44) #102522
│└ Re[2]: 時間のかかる処理の進行の表示 /焼いも (23/11/12(Sun) 17:56) #102523
│  └ Re[3]: 時間のかかる処理の進行の表示 /WebSurfer (23/11/12(Sun) 18:19) #102524
│    └ Re[4]: 時間のかかる処理の進行の表示 /焼いも (23/11/12(Sun) 23:36) #102526
│      └ Re[5]: 時間のかかる処理の進行の表示 /WebSurfer (23/11/12(Sun) 23:53) #102528
Re[1]: 時間のかかる処理の進行の表示 /Azulean (23/11/12(Sun) 22:13) #102525
│└ Re[2]: 時間のかかる処理の進行の表示 /焼いも (23/11/12(Sun) 23:42) #102527
Re[1]: 時間のかかる処理の進行の表示 /KOZ (23/11/13(Mon) 02:38) #102529
  ├ Re[2]: 時間のかかる処理の進行の表示 /KOZ (23/11/13(Mon) 02:48) #102530
  │├ Re[3]: 時間のかかる処理の進行の表示 /WebSurfer (23/11/13(Mon) 09:44) #102531
  │└ Re[3]: 時間のかかる処理の進行の表示 /焼いも (23/11/13(Mon) 18:59) #102533
  │  └ Re[4]: 時間のかかる処理の進行の表示 /KOZ (23/11/14(Tue) 02:42) #102535
  │    └ Re[5]: 時間のかかる処理の進行の表示 /焼いも (23/11/14(Tue) 18:37) #102537
  │      ├ Re[6]: 時間のかかる処理の進行の表示 /KOZ (23/11/15(Wed) 09:55) #102541
  │      │└ Re[7]: 時間のかかる処理の進行の表示 /焼いも (23/11/16(Thu) 22:31) #102555 解決済み
  │      └ Re[6]: 時間のかかる処理の進行の表示 /kiku (23/11/15(Wed) 11:12) #102542
  │        └ Re[7]: 時間のかかる処理の進行の表示 /焼いも (23/11/16(Thu) 15:47) #102552 解決済み
  └ Re[2]: 時間のかかる処理の進行の表示 /焼いも (23/11/13(Mon) 18:31) #102532
    └ Re[3]: 時間のかかる処理の進行の表示 /Azulean (23/11/14(Tue) 00:04) #102534
      └ Re[4]: 時間のかかる処理の進行の表示 /焼いも (23/11/14(Tue) 18:01) #102536


親記事 / ▼[ 102522 ] ▼[ 102525 ] ▼[ 102529 ]
■102521 / 親階層)  時間のかかる処理の進行の表示
□投稿者/ 焼いも (1回)-(2023/11/12(Sun) 15:19:12)

分類:[VB.NET/VB2005 以降] 

Windows11/VB2017を趣味で楽しんでる素人です。
場所違いでしたらご容赦ください。

実行中にフリーズ等がする起因の程はようやく分かったのですが、
これを今あるプログラムにどのように当てはめれば良いのかが分からずにいます。
FunctionやSubは有っても良いのですが、原型は出来るだけ崩さずにする方法があればお願いを致します。

      Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        ProgressBar1.Minimum = 0
        ProgressBar1.Maximum = 1000
        ProgressBar1.Value = 0
        Label1.Text = "0"

        For i = 0 To 1000
            'ここに時間のかかる処理   (※ この場所は変えずにお願いします)
            System.Threading.Thread.Sleep(10)

            ProgressBar1.Value = i
            Me.Refresh()
            Label1.Text = i

        Next
    End Sub

[ □ Tree ] 返信 編集キー/

▲[ 102521 ] / ▼[ 102523 ]
■102522 / 1階層)  Re[1]: 時間のかかる処理の進行の表示
□投稿者/ WebSurfer (2796回)-(2023/11/12(Sun) 15:44:15)
No102521 (焼いも さん) に返信

何を何で作っているのか不明ですが、Winforms または WPF アプリを Visual
Studio 2017 で、ターゲットフレームワークを .NET Framework 4.5 以降で作
っていると想像して。(そういうことは最初に質問に書きましょうね)

> 実行中にフリーズ等がする起因の程はようやく分かったのですが、
> これを今あるプログラムにどのように当てはめれば良いのかが分からずにいます。

解決策は非同期プログラミングです。時間のかかる処理を、UI スレッドではな
く、別のスレッドで実行し、UI スレッドをブロックしないようにするのです。

上の想像が当たっていれば以下の記事が参考になると思います。

WPF/Windowsフォーム:時間のかかる処理をバックグラウンドで実行するには?(async/await編)
https://atmarkit.itmedia.co.jp/ait/articles/1512/02/news019.html
[ 親 102521 / □ Tree ] 返信 編集キー/

▲[ 102522 ] / ▼[ 102524 ]
■102523 / 2階層)  Re[2]: 時間のかかる処理の進行の表示
□投稿者/ 焼いも (2回)-(2023/11/12(Sun) 17:56:38)
2023/11/12(Sun) 18:16:32 編集(投稿者)
No102522 (WebSurfer さん) に返信

> Studio 2017 で、ターゲットフレームワークを .NET Framework 4.5 以降で作
> っていると想像して。(そういうことは最初に質問に書きましょうね)
Windows11はてっきり .NET Framework 4.5 以降と思っていました。

> 解決策は非同期プログラミングです。
そのようです。

時間のかかる処理はFor〜Nextの中に書きたくも思っています。
>       Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
>         ProgressBar1.Minimum = 0
>         ProgressBar1.Maximum = 1000
>         ProgressBar1.Value = 0
>         Label1.Text = "0"
> 
>         For i = 0 To 1000
>             'ここに時間のかかる処理   (※ この場所は変えずにお願いします)
>             System.Threading.Thread.Sleep(10)
> 
>             ProgressBar1.Value = i
>             Me.Refresh()
>             Label1.Text = i
> 
>         Next


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

▲[ 102523 ] / ▼[ 102526 ]
■102524 / 3階層)  Re[3]: 時間のかかる処理の進行の表示
□投稿者/ WebSurfer (2797回)-(2023/11/12(Sun) 18:19:03)
No102523 (焼いも さん) に返信

> Windows11はてっきり .NET Framework 4.5 以降と思っていました。

> はい、それだと思います。

> 時間のかかる処理はFor〜Nextの中に書きたくも思っています。

で、何が分からないのですか? 

自分が今「何がわからないのか」を整理してもらって、それを言語化して
いただけませんか? それに対して回答しますので。

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

▲[ 102524 ] / ▼[ 102528 ]
■102526 / 4階層)  Re[4]: 時間のかかる処理の進行の表示
□投稿者/ 焼いも (3回)-(2023/11/12(Sun) 23:36:11)
No102524 (WebSurfer さん) に返信

この教えていただいたので動作はするのは確認できました。
ただ理想はこのFunction内ある「時間のかかる処理」を、当初のようにFor〜Nextの中に書きたかったのですが
無理なようなので諦めることにします。
何故ならば、ひとつのFormの中にこの処理が複数あり、便宜上は簡単だったからです。

> WPF/Windowsフォーム:時間のかかる処理をバックグラウンドで実行するには?(async/await編)
> https://atmarkit.itmedia.co.jp/ait/articles/1512/02/news019.html
[ 親 102521 / □ Tree ] 返信 編集キー/

▲[ 102526 ] / 返信無し
■102528 / 5階層)  Re[5]: 時間のかかる処理の進行の表示
□投稿者/ WebSurfer (2798回)-(2023/11/12(Sun) 23:53:38)
No102526 (焼いも さん) に返信

> 無理なようなので諦めることにします。

厳しいことを言いますが、それはたぶんあなたの知識では無理と思ってい
るだけでは? 何かやり方はありそうな気がしますけど。

だから、先のレスで、

> 自分が今「何がわからないのか」を整理してもらって、それを言語化して
> いただけませんか? それに対して回答しますので。

と書きましたけど、やる気なしですか? だったら「諦める」というのが
結論ということでいうことで、このスレッドは「解決済み」マークをつけ
てください。
[ 親 102521 / □ Tree ] 返信 編集キー/

▲[ 102521 ] / ▼[ 102527 ]
■102525 / 1階層)  Re[1]: 時間のかかる処理の進行の表示
□投稿者/ Azulean (1287回)-(2023/11/12(Sun) 22:13:49)
2023/11/12(Sun) 22:14:40 編集(投稿者)

No102521 (焼いも さん) に返信
> 実行中にフリーズ等がする起因の程はようやく分かったのですが、
> これを今あるプログラムにどのように当てはめれば良いのかが分からずにいます。
> FunctionやSubは有っても良いのですが、原型は出来るだけ崩さずにする方法があればお願いを致します。


ありません。

Windows において、フォームやコントロールを起点とするイベント(Click イベントなど)は、その外に戻らない限り、「固まります」。
Click イベントから外に逃がさない状態ではユーザーに操作に応答できず、「応答なし」と白くなるのが「普通」です。


長い処理をやりたいなら、原型を崩してください。
あなたしか使わないようなプログラムで応答なしになっても構わないなら、原型を維持して気にしなければ良いでしょう。
[ 親 102521 / □ Tree ] 返信 編集キー/

▲[ 102525 ] / 返信無し
■102527 / 2階層)  Re[2]: 時間のかかる処理の進行の表示
□投稿者/ 焼いも (4回)-(2023/11/12(Sun) 23:42:08)
No102525 (Azulean さん) に返信
> 2023/11/12(Sun) 22:14:40 編集(投稿者)

> ありません。
>
> 長い処理をやりたいなら、原型を崩してください。
> あなたしか使わないようなプログラムで応答なしになっても構わないなら、原型を維持して気にしなければ良いでしょう。

如何に趣味で楽しんでるとは言えそこが一番の問題なのです。

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

▲[ 102521 ] / ▼[ 102530 ] ▼[ 102532 ]
■102529 / 1階層)  Re[1]: 時間のかかる処理の進行の表示
□投稿者/ KOZ (425回)-(2023/11/13(Mon) 02:38:05)
2023/11/13(Mon) 02:42:10 編集(投稿者)
これでいいのでは?

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    ProgressBar1.Minimum = 0
    ProgressBar1.Maximum = 1000
    ProgressBar1.Value = 0
    Label1.Text = "0"
    Await Task.Run(
        Sub()
            For i = 0 To 1000
                System.Threading.Thread.Sleep(10)
                Invoke(
                    Sub(index As Integer)
                        ProgressBar1.Value = index
                        Label1.Text = index.ToString()
                        Me.Refresh()
                    End Sub, i)
            Next
        End Sub)
End Sub

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

▲[ 102529 ] / ▼[ 102531 ] ▼[ 102533 ]
■102530 / 2階層)  Re[2]: 時間のかかる処理の進行の表示
□投稿者/ KOZ (426回)-(2023/11/13(Mon) 02:48:10)
No102529 (KOZ) に返信
こういう形でもいいんでしょうけどメチャ遅くなりそう

For i = 0 To 1000
    Await Task.Run(
        Sub()
            System.Threading.Thread.Sleep(10)
        End Sub)
    ProgressBar1.Value = i
    Label1.Text = i.ToString()
    Me.Refresh()
Next

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

▲[ 102530 ] / 返信無し
■102531 / 3階層)  Re[3]: 時間のかかる処理の進行の表示
□投稿者/ WebSurfer (2799回)-(2023/11/13(Mon) 09:44:27)
No102530 (KOZ さん) に返信

> こういう形でもいいんでしょうけどメチャ遅くなりそう
>
> For i = 0 To 1000
> Await Task.Run(
> Sub()
> System.Threading.Thread.Sleep(10)
> End Sub)
> ProgressBar1.Value = i
> Label1.Text = i.ToString()
> Me.Refresh()
> Next

一つずつ await しないで、Task.Run メソッドを使って 1000 個キューに配置して
から、全部の終了を await Task.WhenAll(...) で待機すれば、逆に、かなり早く
なるかもしれません。

ただし、1000 個の各々にメソッドを実行する順番が決まっている場合はダメですが。
あと、進捗の表示も問題がありそうですが。

具体例は以下の記事の TaskRunAsync メソッドにありますので、興味があれば見て
ください。

タスク並列ライブラリ (TPL) その 2
http://surferonwww.info/BlogEngine/post/2021/07/20/task-parallel-library-2nd.aspx
[ 親 102521 / □ Tree ] 返信 編集キー/

▲[ 102530 ] / ▼[ 102535 ]
■102533 / 3階層)  Re[3]: 時間のかかる処理の進行の表示
□投稿者/ 焼いも (6回)-(2023/11/13(Mon) 18:59:32)
No102530 (KOZ さん) に返信

何故かこれはエラーが出てしまいましたが、No102529ので十分ではあります。

    Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        ProgressBar1.Minimum = 0
        ProgressBar1.Maximum = 1000
        ProgressBar1.Value = 0
        Label1.Text = "0"
        Await Task.Run(
            action:=Async Sub()
                        For i = 0 To 1000
                            Await Task.Run(
        Sub()
            System.Threading.Thread.Sleep(10)
        End Sub)
                            ProgressBar1.Value = i
                            Label1.Text = i.ToString()
                            Me.Refresh()              '< ---ここに「ユーザーが処理していない例外」が出る
                        Next     
                    End Sub)
    End Sub

> ■No102529 (KOZ) に返信
> こういう形でもいいんでしょうけどメチャ遅くなりそう
> 
> For i = 0 To 1000
>     Await Task.Run(
>         Sub()
>             System.Threading.Thread.Sleep(10)
>         End Sub)
>     ProgressBar1.Value = i
>     Label1.Text = i.ToString()
>     Me.Refresh()
> Next
> 

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

▲[ 102533 ] / ▼[ 102537 ]
■102535 / 4階層)  Re[4]: 時間のかかる処理の進行の表示
□投稿者/ KOZ (427回)-(2023/11/14(Tue) 02:42:10)
No102533 (焼いも さん) に返信
> 何故かこれはエラーが出てしまいましたが、No102529ので十分ではあります。

注意事項は2つだけです。
(1) Task.Run の中に書いた処理は別スレッドで処理される。
(2) 別スレッドからコントロールを操作する場合は Invoke しないとエラーになる。

もっとも(2)については、Azulean さんのレスにあるように、デバッガがアタッチしたときチェックされるだけです。
Control.CheckForIllegalCrossThreadCalls プロパティを False にしてデバッグ時にバイパスすることができますが、.NET 1.x のソースを動かすために用意されたものなので、いじらないほうが良いです。

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

▲[ 102535 ] / ▼[ 102541 ] ▼[ 102542 ]
■102537 / 5階層)  Re[5]: 時間のかかる処理の進行の表示
□投稿者/ 焼いも (8回)-(2023/11/14(Tue) 18:37:47)
No102535 (KOZ さん) に返信

ありがとうございます。
No102529ので十分ですのでそちらを使わせていただきます。

それとこれはProgressBar1.Valueを直接設定したもですが、これも同じように可能でしょうか?

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        ProgressBar1.Minimum = 0
        ProgressBar1.Maximum = 100
        ProgressBar1.Value = 0
        Label1.Text = "0"
        Me.Refresh()

        System.Threading.Thread.Sleep(500)   'ここに時間のかかる処理  
        ProgressBar1.Value = 25
        Me.Refresh()
        Label1.Text = 25.ToString()

        System.Threading.Thread.Sleep(500)   'ここに時間のかかる処理  
        ProgressBar1.Value = 50
        Me.Refresh()
        Label1.Text = 50.ToString()

        System.Threading.Thread.Sleep(500)   'ここに時間のかかる処理 
        ProgressBar1.Value = 75
        Me.Refresh()
        Label1.Text = 75.ToString()

        System.Threading.Thread.Sleep(500)   'ここに時間のかかる処理  
        ProgressBar1.Value = 100
        Me.Refresh()
        Label1.Text = 100.ToString()
  
   End Sub





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

▲[ 102537 ] / ▼[ 102555 ]
■102541 / 6階層)  Re[6]: 時間のかかる処理の進行の表示
□投稿者/ KOZ (429回)-(2023/11/15(Wed) 09:55:17)
No102537 (焼いも さん) に返信
> それとこれはProgressBar1.Valueを直接設定したもですが、これも同じように可能でしょうか?

応用問題です。自分で考えてみましょう。

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

▲[ 102541 ] / 返信無し
■102555 / 7階層)  Re[7]: 時間のかかる処理の進行の表示
□投稿者/ 焼いも (10回)-(2023/11/16(Thu) 22:31:19)
No102541 (KOZ さん) に返信

お礼を言うのを遅れました。
ありがとうございました。

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

▲[ 102537 ] / ▼[ 102552 ]
■102542 / 6階層)  Re[6]: 時間のかかる処理の進行の表示
□投稿者/ kiku (383回)-(2023/11/15(Wed) 11:12:54)
No102537 (焼いも さん) に返信
> ■No102535 (KOZ さん) に返信
> ありがとうございます。
> No102529ので十分ですのでそちらを使わせていただきます。
> それとこれはProgressBar1.Valueを直接設定したもですが、これも同じように可能でしょうか?

下記のようにすればできるはずです。
UIスレッド、別スレッド、invoke、Async、Awaitをきちんと学ばないと、
応用問題ができなくなります。
基礎を学ぶことを強くお勧めします。

※下記ではinvoke出てきていませんが、必要な知識です。
※Button1_Click内の処理はUIスレッド上で実行されます。
※Task.Run内の処理は別スレッド上で実行されます。
※Awaitがあると、処置が完了するまで、UIスレッドをブロックすることなく
 Button1_Click内の処理を一時中断します。
※上記言葉が技術的ではないところもありますが、イメージとしてとらえてください。

    Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        ProgressBar1.Minimum = 0
        ProgressBar1.Maximum = 100
        ProgressBar1.Value = 0
        Label1.Text = "0"
        Me.Refresh()

        Await Task.Run(
            Sub()
                System.Threading.Thread.Sleep(500)   'ここに時間のかかる処理
            End Sub)
        ProgressBar1.Value = 25
        Label1.Text = 25.ToString()
        Me.Refresh()

       以下、上記と同様に修正

        System.Threading.Thread.Sleep(500)   'ここに時間のかかる処理  
        ProgressBar1.Value = 50
        Me.Refresh()
        Label1.Text = 50.ToString()

        System.Threading.Thread.Sleep(500)   'ここに時間のかかる処理 
        ProgressBar1.Value = 75
        Me.Refresh()
        Label1.Text = 75.ToString()

        System.Threading.Thread.Sleep(500)   'ここに時間のかかる処理  
        ProgressBar1.Value = 100
        Me.Refresh()
        Label1.Text = 100.ToString()
  
   End Sub

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

▲[ 102542 ] / 返信無し
■102552 / 7階層)  Re[7]: 時間のかかる処理の進行の表示
□投稿者/ 焼いも (9回)-(2023/11/16(Thu) 15:47:19)
2023/11/16(Thu) 17:05:04 編集(投稿者)

No102542 (kiku さん) に返信

> 応用問題ができなくなります。
> 基礎を学ぶことを強くお勧めします。

その通りだと思います。
至らぬことばかりで申し訳ないです。
希望通りに作動いたしました。
ありがとうございます。


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

▲[ 102529 ] / ▼[ 102534 ]
■102532 / 2階層)  Re[2]: 時間のかかる処理の進行の表示
□投稿者/ 焼いも (5回)-(2023/11/13(Mon) 18:31:16)
No102529 (KOZ さん) に返信

これは凄い!
もう諦めてただけにメチャクチャ嬉しいです。
30分近くも掛かる処理なので尚更です。

申し訳ないのですが、もしよかったら関連してのもう一つお願いは出来ないでしょうか?
それは「Label2.Text = "テスト"」のとこで下のエラーがことです。

> ユーザーが処理していない例外
> System.InvalidOperationException: '有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール 'Label2' がアクセスされました。

    Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        ProgressBar1.Minimum = 0
        ProgressBar1.Maximum = 1000
        ProgressBar1.Value = 0
        Label1.Text = "0"
        Await Task.Run(
            Sub()
                For i = 0 To 1000
                    System.Threading.Thread.Sleep(10)
                    Call test()

                    Invoke(
                        Sub(index As Integer)
                            ProgressBar1.Value = index
                            Label1.Text = index.ToString()
                            Me.Refresh()
                        End Sub, i)
                Next
            End Sub)
    End Sub

    'ここでもう一つの作業を実施
    Private Sub test()
        Label2.Text = "テスト"        '< ---ここに「ユーザーが処理していない例外」が出る
    End Sub

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

▲[ 102532 ] / ▼[ 102536 ]
■102534 / 3階層)  Re[3]: 時間のかかる処理の進行の表示
□投稿者/ Azulean (1288回)-(2023/11/14(Tue) 00:04:05)
2023/11/14(Tue) 00:07:22 編集(投稿者)

No102532 (焼いも さん) に返信
> 申し訳ないのですが、もしよかったら関連してのもう一つお願いは出来ないでしょうか?
> それは「Label2.Text = "テスト"」のとこで下のエラーがことです。
>
>>ユーザーが処理していない例外
>>System.InvalidOperationException: '有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール 'Label2' がアクセスされました。


そういうものです。

・Task.Run の中身は「別スレッド」で実行しています。
・「別スレッド」ではコントロールに対する操作はできません。
・「別スレッド」なので、もう一回ボタンをクリックできてしまいますので、対策が必要です。
・「別スレッド」から結果報告・進捗同期のための処理は最小にしないとまたフリーズしますが、仕組みの理解が甘いままでは作れません。
・まだ、最初の状態がマシでしょう。


Task を使おうと、マルチスレッドへの理解は必須なので、あえて前回「無理です」と言いました。


-----
自分しか使わないプログラムで邪道に進んでも良いなら、exe を直接実行すればよろしい。
デバッガをアタッチしなければ、このチェック機能はデフォルト無効になります。
[ 親 102521 / □ Tree ] 返信 編集キー/

▲[ 102534 ] / 返信無し
■102536 / 4階層)  Re[4]: 時間のかかる処理の進行の表示
□投稿者/ 焼いも (7回)-(2023/11/14(Tue) 18:01:10)
No102534 (Azulean さん) に返信

そういうものなのですね。
Labelについては省略するなりします。


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


管理者用

- Child Tree -