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

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

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

Re[14]: フォームの連続的な書き換えに関して


(過去ログ 152 を表示中)

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

■88546 / inTopicNo.1)  フォームの連続的な書き換えに関して
  
□投稿者/ パヨンパヨン (1回)-(2018/09/06(Thu) 19:03:27)

分類:[.NET 全般] 

テキストボックスに表示される数値を連続的に書き換えるプログラムを作りたいのですが、

普通にこういうプログラムや

    Private Sub Button_Click(sender As Object, e As EventArgs) Handles Button1.Click

        For i = 1 To 100

            TextBox4.Text = i.ToString
            Thread.Sleep(10)

        Next

    End Sub

マルチスレッドを使った方法では最後に代入された値しか表示されません。

    Private Sub Button_Click(sender As Object, e As EventArgs) Handles Button1.Click

        Task.Factory.StartNew(
               Sub()

                   For i = 1 To 100

                       TextBox4.Text = i.ToString
                       Thread.Sleep(10)

                   Next

               End Sub)


    End Sub


一方で、



    Private Sub Button_Click(sender As Object, e As EventArgs) Handles Button1.Click

        MultiProgram_run = New Thread(Sub() MultiProgram()) With {.IsBackground = True}
        MultiProgram_run.Start()

    End Sub


    Private Sub MultiProgram()

        For i = 1 To 100

            frm4.BeginInvoke(TextBox4, i.ToString)
            Thread.Sleep(10)

        Next

    End Sub

のようにバックグラウンドプロセスにしてやって、
Invokeで書き換えてやるとうまく表示させることができます。

これは一体なぜなのでしょうか?


引用返信 編集キー/
■88548 / inTopicNo.2)  Re[1]: フォームの連続的な書き換えに関して
□投稿者/ 魔界の仮面弁士 (1822回)-(2018/09/06(Thu) 20:08:55)
No88546 (パヨンパヨン さん) に返信
> テキストボックスに表示される数値を連続的に書き換えるプログラムを作りたいのですが、

Timer を使うのは駄目ですか?


> 最後に代入された値しか表示されません。

TextBox への描画処理は Idle 時、すなわち、
他に処理が行われていない待機状態の時に行われるためです。

ループ処理中に Text を書き換えたとしても、
その書き換えられた値が画面に表示されるのは、
Sub〜End Sub を抜けた後となります。


非アイドル時に、描画依頼やキーボード入力などがおこなれた場合、
実際にそれが処理されるのは、アイドル状態に戻ってからのことです。
このため、描画処理も最後の一回分のみが反映されることになります。


一応、ループ中に TextBox1.Refresh() を呼び出すなどすれば、
強制的に再描画させることもできなくは無いですけれども。


> Private Sub Button_Click(sender As Object, e As EventArgs) Handles Button1.Click
>   MultiProgram_run = New Thread(Sub() MultiProgram()) With {.IsBackground = True}
>   MultiProgram_run.Start()
> End Sub
ワーカースレッドに作業依頼をした後、UI スレッド側は直ちに End Sub に到達していますので
UI スレッドがアイドル状態に戻り、Windows メッセージを処理できる状態になっていたということです。


Task ベースで行いたいなら、こう書けます。(.NET Framework 4.5 以降が必要です)

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
  For i = 1 To 100
    TextBox1.Text = i.ToString()
    Await Task.Delay(10)
  Next
End Sub
引用返信 編集キー/
■88562 / inTopicNo.3)  Re[2]: フォームの連続的な書き換えに関して
□投稿者/ パヨンパヨン (1回)-(2018/09/07(Fri) 21:37:46)
ありがとうございます。

描画処理がアイドル時に行われるのは理解しているのですが、
それだと

Private Sub Button_Click(sender As Object, e As EventArgs) Handles Button1.Click

Task.Factory.StartNew(
Sub()

For i = 1 To 100

TextBox4.Text = i.ToString
Thread.Sleep(10)

Next

End Sub)


End Sub

このプログラムだと、Taskを投げた後に
すぐにEnd Subでアイドルモードになっていると思うのですが
なぜ描画されないのでしょうか?

引用返信 編集キー/
■88564 / inTopicNo.4)  Re[3]: フォームの連続的な書き換えに関して
□投稿者/ 魔界の仮面弁士 (1827回)-(2018/09/07(Fri) 22:30:37)
No88562 (パヨンパヨン  さん) に返信
> このプログラムだと、Taskを投げた後に
> すぐにEnd Subでアイドルモードになっていると思うのですが
> なぜ描画されないのでしょうか?

そもそも、ワーカースレッドで TextBox4.Text を直接操作している時点で論外です。
これだと、InvalidOperationException の例外が発生して止まってしまいませんか?

デバッガに割り当てずに実行させたり、あるいは
 CheckForIllegalCrossThreadCalls = False
を行っておけば、一応動いてはしまいますが…。


元のコードに近い書き方とするなら、

    Private Sub Button_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Task.Factory.StartNew(
                Sub()
                    For i = 1 To 100
                        SetText(i)
                        Thread.Sleep(10)
                    Next
                End Sub)
    End Sub

    Private Sub SetText(i As Integer)
        Dim s As String = i.ToString()
        If InvokeRequired Then
            Invoke(Sub() SetText(s))
        Else
            TextBox4.Text = s
        End If
    End Sub

となるでしょう。この場合は途中の数字も描画されるのでは無いでしょうか。


UIスレッドの同期コンテキスト上でTaskを実行した場合は、
最初の Task を使わないバージョンと同様の動作になるので、
最後の結果しか描画されません。

Task.Factory.StartNew(
        Sub()
            For i = 1 To 100
                TextBox4.Text = i.ToString()
                Thread.Sleep(10)
            Next
        End Sub,
        CancellationToken.None,
        TaskCreationOptions.DenyChildAttach,
        TaskScheduler.FromCurrentSynchronizationContext())

引用返信 編集キー/
■88567 / inTopicNo.5)  Re[4]: フォームの連続的な書き換えに関して
□投稿者/ パヨンパヨン (2回)-(2018/09/08(Sat) 13:06:20)
ありがとうございます。

こちらの勘違いでした、
Invokeしないとエラーになりますね。


ところで、一つ気になったことなのですが、
今までInvokeあるいはBeginInvokeするために、
どこかのWebサイトに書かれていたコードを参考にして

    Delegate Sub delegate_SetText(i As Integer)

    Public SetText_dgl As New delegate_SetText(AddressOf SetText)

    Public Sub SetText(i As Integer)
        Form1.TextBox3.Text = i.ToString()
    End Sub


    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click

        Task.Factory.StartNew(
               Sub()

                   For i = 1 To 100

                       frm.BeginInvoke(SetText_dgl, i)
                       Thread.Sleep(10)

                   Next

               End Sub)

    End Sub

のようにして書いていました。

これよりも、提示してくださった以下のコードの方が短くて良いと思います。


    Public Sub SetText(i As Integer)
        Form1.TextBox3.Text = i.ToString()
    End Sub


    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click

        Task.Factory.StartNew(
               Sub()

                   For i = 1 To 100

                       frm.BeginInvoke(Sub() SetText(i))
                       Thread.Sleep(10)

                   Next

               End Sub)

    End Sub

これらのコードは同じ挙動をすると考えて良いのでしょうか?

引用返信 編集キー/
■88568 / inTopicNo.6)  Re[5]: フォームの連続的な書き換えに関して
□投稿者/ パヨンパヨン (3回)-(2018/09/08(Sat) 13:11:43)
でも、この方法だと複数の引数を受け取れないようなのですが
これは仕様でしょうか?
以下のコードだとエラーになります

    Public Sub SetText(TBox As TextBox, i As Integer)
        TBox.Text = i.ToString()
    End Sub



    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click

        Task.Factory.StartNew(
               Sub()

                   For i = 1 To 100


                       frm.BeginInvoke(Sub() SetText(Form1.TextBox3, i))
                       Thread.Sleep(10)

                   Next

               End Sub)

    End Sub


引用返信 編集キー/
■88575 / inTopicNo.7)  Re[6]: フォームの連続的な書き換えに関して
□投稿者/ 魔界の仮面弁士 (1828回)-(2018/09/08(Sat) 18:58:12)
No88568 (パヨンパヨン  さん) に返信
> でも、この方法だと複数の引数を受け取れないようなのですが

受け渡せますよ?

下記の例で行くと、方法 1 がそれにあたりますが、
方法 2 や 3 でも同じ結果が得られます。

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Task.Factory.StartNew(
        Sub()
            For i = 1 To 100
                Dim x As String = i.ToString()
                Dim y As String = (100 - i).ToString()
                Dim z As Color = Color.FromArgb(255 * i \ 100, 128, 128)

                ''方法1
                Invoke(New Action(Of String, String, Color)(AddressOf 複数の引数), x, y, z)

                '方法2
                'Invoke(Sub() 複数の引数(x, y, z))

                ''方法3
                'Invoke(New MethodInvoker(
                '    Sub()
                '        TextBox1.Text = x
                '        TextBox2.Text = y
                '        TextBox1.BackColor = z
                '    End Sub))

                Thread.Sleep(10)
            Next
        End Sub)
End Sub

Private Sub 複数の引数(a As String, b As String, c As Color)
    TextBox1.Text = a
    TextBox2.Text = b
    TextBox1.BackColor = c
End Sub



> 以下のコードだとエラーになります

VB バージョンが不明な点も気になりますが、それには目を瞑るとしても、
どの部分で何というエラーになるのかぐらいは書いて欲しいです…。



> frm.BeginInvoke(Sub() SetText(Form1.TextBox3, i))

【問題点1】ラムダ式の遅延評価と変数のスコープ

まずは引数 i。上記の変数 i はループカウンタですが、
ラムダ式は遅延実行されるものであることに注意してください。
(VB コンパイラのバージョンによっては、これは BC43234 の警告となります)

たとえば、このようなラムダ式があったとします。

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim act(9) As MethodInvoker
        For i As Integer = 0 To 9
            act(i) = Sub() Debug.Print(i)
        Next

        Array.ForEach(act, Sub(method) method())
    End Sub

この場合、0, 1, 2, 3, …, 8, 9 が出力されるようにも思えますが、
実際には、すべて 10 が出力されることになるでしょう。

連番で出力されるためには、
  For i As Integer = 0 To 9
    act(i) = Sub() Debug.Print(i)
  Next
  Array.ForEach(act, Sub(method) method())
ではなく、
  For i As Integer = 0 To 9
    Dim 局所変数 As Integer = i
    act(i) = Sub() Debug.Print(局所変数)
  Next
  Array.ForEach(act, Sub(method) method())
とする必要があります。



> frm.BeginInvoke(Sub() SetText(Form1.TextBox3, i))

【問題点2】既定のフォームインスタンス Form1 と明示的インスタンス frm

Form1.TextBox3 を扱うために、何故 frm の BeginInvoke を呼び出しているのでしょうか。

TextBox3 を操作するために、TextBox3 (またはその親フォーム等)の
BeginInvoke/Invoke を呼ぶのではなく、それとは別のインスタンスで
あると予想される「frm 変数」のインスタンスに対して
実行依頼を投げている点が不自然に思えました。

UI スレッドが一つしか無い場合には、結果的には正しいスレッドで動くのですが、
それならばそもそも、わざわざ frm.BeginInvoke と書く必要は無く、
単に BeginInvoke で良いわけで。(Button2 から呼んでいるのですから)

あるいは逆に、SplashScreen 利用時などといった、
UI スレッドが複数あるようなケースだとしたら、UI スレッドでの操作のために、
TextBox3 (またはその親フォーム等)ではなく、別のインスタンスであると
予想される frm に対して実行依頼を投げているの点が不自然です。


それに、frm は「明示的にインスタンス化されたフォーム変数」であるように
見えますが、Form1 はそうではなく、いわゆる My.Forms.Form1 相当の
「既定のフォームインスタンス」では無いでしょうか。

もしそうなら、このコードの Button2_Click が Form1 内に書かれていた場合、
BC31139 のコンパイルエラーになりうる可能性があります。
いずれにせよ、既定のインスタンスに頼ったコードは避けた方が良いでしょう。
(複数のスレッドをまたぐような場面で使いたいのであれば尚の事)



> frm.BeginInvoke(Sub() SetText(Form1.TextBox3, i))

【問題点3】オブジェクトの管理

BeginInvoke メソッドや Invoke メソッドは、スレッドセーフであることが
保証されているため、どのスレッドから呼び出しても問題にはなりません。

しかし、Form1.TextBox3 というのは、
 Friend WithEvents TextBox3 As TextBox
というフィールド変数ですよね。
この変数へのアクセスはスレッドセーフになっていませんので、
望ましいコードとは言えません。



■No88567 (パヨンパヨン  さん) に返信
> Public Sub SetText(i As Integer)
>     Form1.TextBox3.Text = i.ToString()
> End Sub

「frm.BeginInvoke から呼ばれた SetText」は、
frm が管理されているスレッド上で動作しますから、
そこで呼び出された SetText 内から
Form1 の TextBox3 フィールドにアクセスするのは OK です。

引用返信 編集キー/
■88576 / inTopicNo.8)  Re[7]: フォームの連続的な書き換えに関して
□投稿者/ パヨンパヨン (2回)-(2018/09/08(Sat) 19:27:53)

ありがとうございます。

確認したところ、ご指摘の通り、
frm.BeginInvoke(Sub() SetText(Form1.TextBox3, i))

としてあるのがエラーの原因で、
frm.BeginInvoke(Sub() SetText(Me.TextBox3, i))
とするとうまくいきました。



> 【問題点1】ラムダ式の遅延評価と変数のスコープ

ラムダ式のことは理解しています。
ご指摘ありがとうございます。


> 【問題点2】既定のフォームインスタンス Form1 と明示的インスタンス frm

> Form1.TextBox3 を扱うために、何故 frm の BeginInvoke を呼び出しているのでしょうか。

これはフォーム上に全てのコードを書いてしまうと長くなって読みづらいので
普段は、別のモジュール上にコードを書いているためです。
他のコードをコピーして流用したため、残ってしまいました。


> 【問題点3】オブジェクトの管理

そうですね、Taskの中でエラーが起こると、
どの箇所にエラーが起きたのか明示的に表示されないので
不便です。




念のため、再度確認なのですが、

''方法1
Invoke(New Action(Of String, String, Color)(AddressOf 複数の引数), x, y, z)

'方法2
'Invoke(Sub() 複数の引数(x, y, z))

''方法3
'Invoke(New MethodInvoker(
' Sub()
' TextBox1.Text = x
' TextBox2.Text = y
' TextBox1.BackColor = z
' End Sub))


という三つの方法をご提示くださりましたが、
これらはどれを使っても計算速度やPC負荷などに全く違いは見られないと考えて良いのでしょうか?
単純に考えると

'方法2
'Invoke(Sub() 複数の引数(x, y, z))

がもっともコードが短く単純なので、ベストチョイスのように思えますが、
使い分けなどがございますでしょうか?


引用返信 編集キー/
■88583 / inTopicNo.9)  Re[8]: フォームの連続的な書き換えに関して
□投稿者/ 魔界の仮面弁士 (1830回)-(2018/09/08(Sat) 23:13:39)
こちらで実験すると、再現するどころかエラーになってしまったので、
ちょっと悩んでいたのですが、投稿いただいた質問コードは
実際のコードと同一というわけではなかったのですね。

No88576 (パヨンパヨン さん) に返信
> としてあるのがエラーの原因で、
> frm.BeginInvoke(Sub() SetText(Me.TextBox3, i))
> とするとうまくいきました。

いずれにせよ、マルチスレッドを意識したコードにするのなら、
フォームの「既定のインスタンス」を使わないコーディングに慣れておいた方が良いですよ。


>>【問題点1】ラムダ式の遅延評価と変数のスコープ
> ラムダ式のことは理解しています。
一番最初の質問である
 「最後に代入された値しか表示されません。」
の要因かとも思ったので、一応指摘させていただきました。


>>【問題点2】既定のフォームインスタンス Form1 と明示的インスタンス frm
>>Form1.TextBox3 を扱うために、何故 frm の BeginInvoke を呼び出しているのでしょうか。
> これはフォーム上に全てのコードを書いてしまうと長くなって読みづらいので

Form に書くべき処理は、本来はそのフォーム自身のふるまいだけなので、
ビジネスロジック等は、別の Class や Module に分けたりしますね。

それでも Form のコードが多くなってしまう場合には、
Partial で分割するという方法もあるようです。


> 普段は、別のモジュール上にコードを書いているためです。
> 他のコードをコピーして流用したため、残ってしまいました。

frm が Shared な変数あるいは Module 上の変数だとすると、
同一フォームの複数インスタンスが生成されていた場合には
都合が悪いかも知れません。


>> ''方法3
> 'Invoke(New MethodInvoker(
> ' Sub()
ちなみにこれは、
Invoke(New Action(Sub()

Invoke(Sub()
でも OK です。いずれも同じ意味になります。

※上記は MyBase.Invoke の意味なので、Module 等に記述する場合には
 対象コントロールの Invoke メソッドの呼び出しに変更する必要があります。



> '方法2
> 'Invoke(Sub() 複数の引数(x, y, z))
> がもっともコードが短く単純なので、ベストチョイスのように思えますが、

先の例の場合、「Private Sub 複数の引数(…)」を用意していましたよね。

クラスやモジュールに、再利用性の無い(かつ、比較的短い処理の)メソッドが
多数存在してしまうことを嫌うような場合においては、方法3 のように、
ラムダ式やローカル関数で済ませる場合もあるということです。

ちなみにローカル関数は VB には無い機能です。(C#7.0 以降)



> という三つの方法をご提示くださりましたが、
> これらはどれを使っても計算速度やPC負荷などに全く違いは見られないと考えて良いのでしょうか?
厳密にいえば、まったく同じコンパイル結果になるわけではありません。
パフォーマンス差については、各種条件によってかわってくるものなので、
ご自身で実際に検証頂いた上でご判断ください。

ただ、手順や仕組みはどうあれ、同じ結果を得られるのであれば、
その時々で使いやすいものを選んでいけば良いのではないでしょうか。


で。直接的な速度差はさておき、コンパイル結果がもっとも単純となるのは方法1 です。

>> '方法1
>> Invoke(New Action(Of String, String, Color)(AddressOf 複数の引数), x, y, z)
これは匿名型等は必要とせず、元のコードのとおりに、
 Dim act As Action(Of String, String, Color) = AddressOf Me.複数の引数
 MyBase.Invoke(act, New Object() { x, y, z })
のような形にコンパイルされるだけで済みます。
(実際には act という変数が作られるわけではないですが、説明のために付加しています)


しかし方法2 や 3 の場合は、局所変数である x/y/z の 3 つ(それと、Me を含めた 4 つ)の値を
フィールド変数として閉包した匿名クロージャと、匿名デリゲートが作成されることになります。

たとえば
>> '方法2
>> Invoke(Sub() 複数の引数(x, y, z))
というコードのコンパイル結果は、Visual Basic 的な文法イメージでいうと
 Dim closure As New 匿名型() With { .[Self] = Me, .[x] = x, .[y] = y, .[z] = z }
 Dim act As 匿名デリゲート = AddressOf closure.Lamda
 MyBase.Invoke(act, New Object() { x, y, z })
のようになるため、手順的には方法 1 よりも一手間増えます。

また、これの呼び出しのために
 Partial Class Form1
  Friend Delegate Sub 匿名デリゲート()
  Friend Class 匿名型
   Public [Self] As Form1
   Public [x] As String
   Public [y] As String
   Public [z] As Color
   Friend Sub Lamda()
    Me.[Self].複数の引数(Me.[x], Me.[y], Me.[z])
   End Sub
  End Class
 End Class
に相当する物が内部的に作られることにもなりますね。
引用返信 編集キー/
■88590 / inTopicNo.10)  Re[9]: フォームの連続的な書き換えに関して
□投稿者/ パヨンパヨン (3回)-(2018/09/09(Sun) 14:02:40)
ありがとうございます。

自分で速度比較を行ってみました。


    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click

        Dim sw As New Stopwatch
        sw.Reset()
        sw.Start()


        Task.Factory.StartNew(
               Sub()

                   For i = 1 To 10000

                       Dim s As Integer = i

                       '方法1
                       '  BeginInvoke(New Action(Of Control, Integer)(AddressOf SetText), TextBox1, s)     '40〜50ミリ秒

                       '方法2
                       ' BeginInvoke(Sub() SetText(Me.TextBox3, s))           '100〜120ミリ秒

                       '方法3
                       Invoke(New MethodInvoker(                '300ミリ秒
                           Sub()
                           End Sub))


                       '方法4
                       ' BeginInvoke(SetText_dgl, Me.TextBox1, s)         '50〜70ミリ秒


                   Next


                   frm.BeginInvoke(Sub() SetTextBox(Me.TextBox2, sw.ElapsedMilliseconds.ToString))

               End Sub)


    End Sub



以下は別のモジュール

    Delegate Sub delegate_SetText(TBox As TextBox, i As Integer)

    Public SetText_dgl As New delegate_SetText(AddressOf SetText)

    Public Sub SetText(TBox As TextBox, i As Integer)
        'TBox.Text = i.ToString()
    End Sub



ご指摘の通り、方法1の方が方法2よりもほぼ2倍速いことがわかりました。
方法3に至っては6倍くらい遅い結果となりました。
予め、delegateクラスを作成する方法4も比較してみましたが、
なぜか方法1よりも若干だけ遅くなる結果となりました。

ちなみに、本来ならテキストボックスに数値を表示するためのコードなのですが、
 'TBox.Text = i.ToString()
のコメントアウトを外すと以下のエラーが発生してしまいます。


マネージ デバッグ アシスタント 'DisconnectedContext' が 'D:\Documents and Settings\My Documents\xxx.exe' で問題を検出しました。

追加情報:この RuntimeCallableWrapper の COM コンテキスト 0xe8a5f8 への変換に失敗して、エラー システム コールに失敗しました。 (HRESULT からの例外:0x80010100 (RPC_E_SYS_CALL_FAILED)) が発生しました。これは、通常この RuntimeCallableWrapper が作成された COM コンテキスト 0xe8a5f8 が切断されたか、他の処理を実行していてビジーであることが原因です。現在の COM コンテキスト (COM コンテキスト 0xe8a6b0) からインターフェイスを解放しています。これは、破損またはデータの損失を発生させる可能性があります。この問題を回避するには、その内部に存在する COM コンポーネントを示す RuntimeCallableWrappers の使用が完了するまで、すべての COM コンテキスト、アパートメント、およびスレッドが完全に有効であり、コンテキスト変換に使用できることを確認してください。


また、このコメントアウトを外さなかったとしても、

 For i = 1 To 10000
のところを
 For i = 1 To 100000
に変更すると、同じエラーが発生します。

これは一体なぜでしょうか?
エラーが発生しないようにするにはどうしたら良いですか?

引用返信 編集キー/
■88591 / inTopicNo.11)  Re[10]: フォームの連続的な書き換えに関して
□投稿者/ パヨンパヨン (4回)-(2018/09/09(Sun) 14:21:14)
方法3が遅いのは
Inovokeになっているからでした
BeginInvokeにすると100ミリ秒くらいになりました。
引用返信 編集キー/
■88594 / inTopicNo.12)  Re[11]: フォームの連続的な書き換えに関して
□投稿者/ 魔界の仮面弁士 (1834回)-(2018/09/09(Sun) 17:43:00)
No88591 (パヨンパヨン さん) に返信
> 方法3が遅いのは
> Inovokeになっているからでした
> BeginInvokeにすると100ミリ秒くらいになりました。
BeginInvoke は非同期実行なので、呼び出した処理が完了しないうちに、
直ちに呼び出し元の次の行が実行されてしまうことなります。

同期的な待ち合わせが必要なら、
 Dim ar As IAsyncResult = MyBase.BeginInvoke(……)
 Thread.Sleep(10)
 Dim result As Object = MyBase.EndInvoke(ar)
といった書き方を使うこともできます。

スレッドプールは有限なのでむやみやたらに BeginInvoke を
投げっぱなしでループで呼び続けるのは、いくら何でも
乱暴すぎますように思います。
そもそも UI スレッドは、基本的に単一スレッドですし…。

No88575 の私の例でも、BeginInvoke は使っていませんよね。



No88590 (パヨンパヨン さん) に返信
> Dim sw As New Stopwatch
> sw.Reset()
> sw.Start()
これは
 Dim sw = Stopwatch.StartNew()
とも書けますね。


> Delegate Sub delegate_SetText(TBox As TextBox, i As Integer)
delegate_SetText 型は Action(Of TextBox, Integer) 型と同義ですね。


> (HRESULT からの例外:0x80010100 (RPC_E_SYS_CALL_FAILED)) が発生しました。
このエラーが発生する要因は一つではないため特定はできませんが、たとえば
非同期呼び出しの頻度が多すぎてビジー状態に陥った際に発生することがあるようです。



> For i = 1 To 10000
> のところを
> For i = 1 To 100000
> に変更すると、同じエラーが発生します。
手元の環境(Core i7-8650U / 4 コア 8 スレッド)では再現しませんでしたが、このへんの値は、環境にもよるのでしょうね。

下記は Control.BeginInvoke ではなく、Delegate.BeginInvoke の話題ですが、参考までに。
http://d.hatena.ne.jp/akiramei/20060123/p1
引用返信 編集キー/
■88602 / inTopicNo.13)  Re[10]: フォームの連続的な書き換えに関して
□投稿者/ 魔界の仮面弁士 (1838回)-(2018/09/10(Mon) 10:59:57)
No88590 (パヨンパヨン さん) に返信
> 以下は別のモジュール
> Delegate Sub delegate_SetText(TBox As TextBox, i As Integer)
> Public SetText_dgl As New delegate_SetText(AddressOf SetText)
> Public Sub SetText(TBox As TextBox, i As Integer)
>   'TBox.Text = i.ToString()
> End Sub

モジュール というのは、VB の Module 〜 End Module のことでしょうか。


自分自身で確認したわけではないのですが:

デリゲートに渡すメソッドは、Module 上のメソッドや Shared メソッドだと効率が劣るため、
パフォーマンスを優先したいのであれば、インスタンスメソッドの方が良いらしいです。
https://ufcpp.net/study/csharp/functional/miscdelegateinternal/#static-method
引用返信 編集キー/
■88603 / inTopicNo.14)  Re[11]: フォームの連続的な書き換えに関して
□投稿者/ パヨンパヨン (5回)-(2018/09/10(Mon) 16:27:01)

ありがとうございます。

ご指摘の通り、

Delegate Sub delegate_SetText(TBox As TextBox, i As Integer)

Public SetText_dgl As New delegate_SetText(AddressOf SetText)

Public Sub SetText(TBox As TextBox, i As Integer)
'TBox.Text = i.ToString()
End Sub

の部分だけを
別の
Module 〜 End Module
に書いているという意味です。

引用返信 編集キー/
■88604 / inTopicNo.15)  Re[12]: フォームの連続的な書き換えに関して
□投稿者/ パヨンパヨン (6回)-(2018/09/10(Mon) 16:29:42)
コードを貼り付けたいのですが
迷惑投稿者というエラーが出て投稿拒否されてしまいます。
どこが問題なのか分かりません。

以下に質問内容とコードをアップしましたので
そちらを見ていただけますでしょうか?

http://fast-uploader.com/file/7092120068054/
Pass: 123456

引用返信 編集キー/
■88605 / inTopicNo.16)  Re[13]: フォームの連続的な書き換えに関して
□投稿者/ 魔界の仮面弁士 (1839回)-(2018/09/10(Mon) 18:04:55)
No88604 (パヨンパヨン さん) に返信
> コードを貼り付けたいのですが
> 迷惑投稿者というエラーが出て投稿拒否されてしまいます。
> どこが問題なのか分かりません。

正:以下の方法/で/合/っていますか?
誤:以下の方法/出/会/っていますか?



> 以下に質問内容とコードをアップしましたので
> そちらを見ていただけますでしょうか?
というか、Option Strict On だとコンパイルエラーになりますよねコレ。


Integer と String の両方に対応させたいなら、
 Public Overloads Sub SetText(TBox As TextBox, s As String)
 End Sub
 Public Overloads Sub SetText(TBox As TextBox, i As Integer)
 End Sub
のようにオーバーロードすべきですし、折角用意した
 Delegate Sub delegate_SetText(TBox As TextBox, i As Integer)
も、まったく使われていないどころか、指定するデリゲート型が
 Action(Of TextBox, Integer)
ではなく
 Action(Of Control, Integer)
になっているため、Control → TextBox の縮小変換が行われちゃいますし…。




そもそも、一連の質問のコードは、どういう処理を意図しているのでしょうか?
10 ミリ秒待機しながら、TextBox の値を書き換えたいのなら、

 Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
  For i = 1 To 10000
   TextBox1.Text = Await Progress(i)
  Next
 End Sub
 
 Public Async Function Progress(i As Integer) As Task(Of String)
  Await Task.Delay(10)
  Return i.ToString()
 End Function

の方が良さそうな。



> frm.Invoke(Sub() SetTextBox(Me.TextBox2, sw.ElapsedMilliseconds.ToString))
また frm が再来してますね。外部の変数を使わずとも、
自フォームの Invoke か、TextBox2 の Invoke で良いような。


> Dim CL As New Class1
> BeginInvoke(New Action(Of Control, Integer)(AddressOf CL.SetText), TextBox1, s)
SetText の第一引数が常に TextBox1 であるのなら、
 Dim CL As New Class1(TextBox1)  '固定パラメータをコンストラクタで渡す
 BeginInvoke(CL.DelegateInstanceOfSetText, s) 'デリゲート型のフィールドを渡す
な実装にした方がすっきりしそう。


> 時間を比較してみましたが、
こちらでは検証していませんが、何故 BeginInvoke を呼んでいるのでしょうか。

IAsyncResult を処理している様子も無さそうなので、これだと、
デリゲートを受け渡す時間が測定されるのみで、
デリゲートの実行時間は測定されない気がします。
引用返信 編集キー/
■88683 / inTopicNo.17)  Re[14]: フォームの連続的な書き換えに関して
□投稿者/ パヨンパヨン (7回)-(2018/09/19(Wed) 21:22:52)
ありがとうございます。
勉強になりました。

> も、まったく使われていないどころか、指定するデリゲート型が
>  Action(Of TextBox, Integer)
> ではなく
>  Action(Of Control, Integer)
> になっているため、Control → TextBox の縮小変換が行われちゃいますし…。


ありがとうございます。
タイプミスです。
Action(Of TextBox, Integer)
にも変えてみましたが速度は変わりませんでした。



> こちらでは検証していませんが、何故 BeginInvoke を呼んでいるのでしょうか。

本流の計算はバックグランドで行っており、
バックグラウンドプロセスの速度さえ速ければ良く、
BeginInvokeで投げたフォアグラウンドプロセスでの速度は問題にならないからです。




解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -