すいません、VB4しかやってないんです、VBAはやったけど(ぼそ) チラシの裏だって立派な書き込み空間なんだからねっ!資源の有効活用なんだからねっ!とか偉そうに言ってるけど、実は色々と書き残したいだけ

だからなに? どうしろと? くるみサイズの脳みそしかないあやしいジャンガリアンベムスターがさすらう贖罪蹂躙(ゴシックペナルティ)

ホーム 連絡をする 同期する ( RSS 2.0 ) Login
投稿数  632  : 記事  35  : コメント  11677  : トラックバック  143

ニュース


片桐 継 は
こんなやつ

かたぎり つぐ ってよむの

大阪生まれ河内育ちなんだけど
関東に住みついちゃったの
和装着付師だったりするの
エセモノカキやってたりするの
VBが得意だったりするの
SQL文が大好きだったりするの
囲碁修行中だったりするの
ボトゲ好きだったりするの
F#かわいいよF#

正体は会った人だけ知ってるの

空気読まなくてごめんなさいなの


わんくまリンク

C#, VB.NET 掲示板
C# VB.NET掲示板

わんくま同盟
わんくま同盟Blog


WindowsでGo言語
WindowsでGo言語


ネット活動


SNSは疲れました

記事カテゴリ

書庫

日記カテゴリ

ギャラリ

イベント活動

プログラムの活動

その1はこちら

やまたのおろちが酒をのむ・リターンズ~片桐的マルチスレッドVBプログラミング~

さて、ここで、スサノオさんがやってきて、やまたのおろちさんの首を落とすことにするw

Imports System
Imports System.Threading

Module OrochMTA

    Delegate Sub DrinkingSAKE()
    Delegate Sub CuttingHead()

    '酒ツボに酒を準備 
    Public inSakeTubo As New SakeTubo(2000)

    <MTAThread()> _
    Public Sub Main()


        '本体で首が別々に酒飲みするのを身構える   
        Dim ts As New ThreadStart(AddressOf OrochiDrinking)

        Dim workerThread As New Thread(ts)

        '首は別々に動けるように、マルチスレッドで宣言
        workerThread.SetApartmentState(ApartmentState.MTA)

        workerThread.Start()

        workerThread.Join()

        Stop

    End Sub

    Private Sub OrochiDrinking()

        Try

            'まずは首を八本で飲んでみる   
            Dim headCount As Integer = 8

            '酔っ払い待ち行列を首の数だけ準備   
            Dim yoppa(headCount - 1) As WaitHandle

            '酒飲み開始
            For i As Integer = 1 To headCount

                '飲んだ首から酔っ払いへと   
                yoppa(i - 1) = HeadDrinking(i).AsyncWaitHandle

            Next

            '全部の首が酔っ払いになるまで待機   
            WaitHandle.WaitAll(yoppa)

        Catch ex As Exception

            Console.WriteLine("首切られた!")

        End Try


    End Sub

    Private Function HeadDrinking(ByVal headNo As Integer) As IAsyncResult

        '指定した番号の首に酒を飲ませる   
        Dim myHead As New OrochiHead(headNo)

        Dim gubi As New DrinkingSAKE(AddressOf myHead.Drink)

        '酒飲み開始とともに、酔っ払い待ち行列へ戻す   
        Return gubi.BeginInvoke(Nothing, Nothing)

    End Function

    Public Class OrochiHead
        'おろちの首クラス

        'お前は何番目なのかを保持
        Private meNumber As Integer

        '何番目の首?初期化w 
        Public Sub New(ByVal headNo As Integer)

            meNumber = headNo

        End Sub

        '酒を飲むメソッド   
        Public Sub Drink()

            Dim nowDrink As Integer
            Dim nowNokori As Integer

            Dim oneDrink As New Random
            Dim oneBreath As New Random

            '酒ツボの中の酒を飲み干す   
            While Not (inSakeTubo.Nomihoshited)

                '一回飲むたびに息継ぎ   
                Thread.Sleep(oneBreath.Next(30, 60))

                '酒つぼから飲んでみる
                If inSakeTubo.Nomihoshited Then
                    Console.WriteLine("{0}番目の首「もう酒がねぇよ!!ぷんすか」", meNumber)
                    Exit Sub
                Else
                    '一回ぐび   
                    nowDrink = oneDrink.Next(1, 100)
                    nowNokori = inSakeTubo.Nokori
                    inSakeTubo.Gubi(nowDrink)
                    If inSakeTubo.Nomihoshited Then
                        Console.WriteLine("{0}番目の首「ちきしょう!足りねぇよ!{1}のみてぇのに{2}しかねぇ!」", meNumber, nowDrink, nowNokori)
                        Exit Sub
                    Else
                        Console.WriteLine("{0}番目の首が {1} リットル ぐびっ : 残り {2} リットル", meNumber, nowDrink, inSakeTubo.Nokori)
                    End If
                End If

                'スサノオさんアップ中
                Dim cutNo As New Random
                Dim cutHead = cutNo.Next(1, 8)

                '運が悪かったら首切りw
                If cutHead = meNumber Then
                    Console.WriteLine("スサノオさんが{0}番目の首切った!", meNumber)
                    Throw New Exception("やまたのおろちさんえくせぷしょん")
                End If

            End While

            Console.WriteLine("ぷはーっ! {0}番目の首、飲み終わり!", meNumber)

        End Sub

    End Class

    Public Class SakeTubo
        '酒つぼクラス
        'マルチスレッドでの読み書き保護処理を集約

        Private cacheLock As New ReaderWriterLockSlim()
        Private innerSake As Integer

        Public Sub New(ByVal litter As Integer)

            innerSake = litter

        End Sub

        Public Function Nokori() As Integer
            cacheLock.EnterReadLock()
            Try
                Return innerSake
            Finally
                cacheLock.ExitReadLock()
            End Try
        End Function

        Public Sub Gubi(ByVal gubigubi As Integer)

            Dim retFlag As Boolean = True

            cacheLock.EnterWriteLock()
            Try
                If innerSake > gubigubi Then
                    innerSake -= gubigubi
                Else
                    innerSake = 0
                End If
            Finally
                cacheLock.ExitWriteLock()
            End Try
        End Sub

        Public Sub Karappo()
            cacheLock.EnterWriteLock()
            Try
                innerSake = 0
            Finally
                cacheLock.ExitWriteLock()
            End Try
        End Sub

        Public Function Nomihoshited() As Boolean
            cacheLock.EnterWriteLock()
            Try
                Return (innerSake.Equals(0))
            Finally
                cacheLock.ExitWriteLock()
            End Try
        End Function

    End Class

End Module

前回とあまり変わってないけど、おろちクラスの中で、運が悪いとスサノオさんに首を切られてExceptionが発生します。

結果はこんな感じ

image

あれ?例外発生してない????

実は、マルチスレッドで動作している中の一つの子スレッドが例外発生して止まっても他の兄弟姉妹スレッドは動き続けるし、スレッドの発生元である親スレッドでその例外をCatchすることができない。だから、実行中に複数のうちの一つの子スレッドでエラーが発生しても見た目には判んないことがあるの。これはとっても大変なこと。

で、私はこれをどうしたかというと、おろちの首が落ちたかどうかを首クラスに情報として持たせておいて、全てのスレッドが終了した後で判定する方法をとってみることにしたの。

Imports System
Imports System.Threading

Module OrochMTA

    Delegate Sub DrinkingSAKE()

    '酒ツボに酒を準備 
    Public inSakeTubo As New SakeTubo(2000)

    'おろちの首準備
    Public orochiHeads As New List(Of OrochiHead)

    <MTAThread()> _
    Public Sub Main()


        '本体で首が別々に酒飲みするのを身構える   
        Dim ts As New ThreadStart(AddressOf OrochiDrinking)

        Dim workerThread As New Thread(ts)

        '首は別々に動けるように、マルチスレッドで宣言
        workerThread.SetApartmentState(ApartmentState.MTA)

        workerThread.Start()

        workerThread.Join()

        Stop

    End Sub

    Private Sub OrochiDrinking()

        'まずは首を八本で飲んでみる   
        Dim headCount As Integer = 8

        '酔っ払い待ち行列を首の数だけ準備   
        Dim yoppa(headCount - 1) As WaitHandle

        orochiHeads.Capacity = headCount

        '酒飲み開始
        For i As Integer = 1 To headCount

            '飲んだ首から酔っ払いへと   
            yoppa(i - 1) = HeadDrinking(i).AsyncWaitHandle

        Next

        '全部の首が酔っ払いになるまで待機   
        WaitHandle.WaitAll(yoppa)

        '切られた首チェック
        For Each head In orochiHeads

            If head.orochiException Is Nothing Then
                Continue For
            End If

            Console.WriteLine(head.orochiException.Message)

        Next

    End Sub

    Private Function HeadDrinking(ByVal headNo As Integer) As IAsyncResult

        '指定した番号の首に酒を飲ませる
        orochiHeads.Add(New OrochiHead(headNo))

        Dim gubi As New DrinkingSAKE(AddressOf orochiHeads(headNo - 1).Drink)

        '酒飲み開始とともに、酔っ払い待ち行列へ戻す   
        Return gubi.BeginInvoke(Nothing, Nothing)

    End Function

    Public Class OrochiHead
        'おろちの首クラス

        '首が無事なのかどうか
        Public orochiException As Exception

        'お前は何番目なのかを保持
        Private meNumber As Integer

        '何番目の首?初期化w 
        Public Sub New(ByVal headNo As Integer)

            meNumber = headNo

        End Sub

        '酒を飲むメソッド   
        Public Sub Drink()

            Dim nowDrink As Integer
            Dim nowNokori As Integer

            Dim oneDrink As New Random
            Dim oneBreath As New Random

            '酒ツボの中の酒を飲み干す   
            While Not (inSakeTubo.Nomihoshited)

                '一回飲むたびに息継ぎ   
                Thread.Sleep(oneBreath.Next(30, 60))

                '酒つぼから飲んでみる
                If inSakeTubo.Nomihoshited Then
                    Console.WriteLine("{0}番目の首「もう酒がねぇよ!!ぷんすか」", meNumber)
                    Exit Sub
                Else
                    '一回ぐび   
                    nowDrink = oneDrink.Next(1, 100)
                    nowNokori = inSakeTubo.Nokori
                    inSakeTubo.Gubi(nowDrink)
                    If inSakeTubo.Nomihoshited Then
                        Console.WriteLine("{0}番目の首「ちきしょう!足りねぇよ!{1}のみてぇのに{2}しかねぇ!」", meNumber, nowDrink, nowNokori)
                        Exit Sub
                    Else
                        Console.WriteLine("{0}番目の首が {1} リットル ぐびっ : 残り {2} リットル", meNumber, nowDrink, inSakeTubo.Nokori)
                    End If
                End If

                'スサノオさんアップ中
                Dim cutNo As New Random
                Dim cutHead = cutNo.Next(1, 8)

                '運が悪かったら首切りw
                If cutHead = meNumber Then
                    Console.WriteLine("スサノオさんが{0}番目の首切った!", meNumber)
                    orochiException = New Exception(String.Format("{0}番目の首は切られてしまった!", meNumber))
                    Exit Sub
                End If

            End While

            Console.WriteLine("ぷはーっ! {0}番目の首、飲み終わり!", meNumber)

        End Sub

    End Class

    Public Class SakeTubo
        '酒つぼクラス
        'マルチスレッドでの読み書き保護処理を集約

        Private cacheLock As New ReaderWriterLockSlim()
        Private innerSake As Integer

        Public Sub New(ByVal litter As Integer)

            innerSake = litter

        End Sub

        Public Function Nokori() As Integer
            cacheLock.EnterReadLock()
            Try
                Return innerSake
            Finally
                cacheLock.ExitReadLock()
            End Try
        End Function

        Public Sub Gubi(ByVal gubigubi As Integer)

            Dim retFlag As Boolean = True

            cacheLock.EnterWriteLock()
            Try
                If innerSake > gubigubi Then
                    innerSake -= gubigubi
                Else
                    innerSake = 0
                End If
            Finally
                cacheLock.ExitWriteLock()
            End Try
        End Sub

        Public Sub Karappo()
            cacheLock.EnterWriteLock()
            Try
                innerSake = 0
            Finally
                cacheLock.ExitWriteLock()
            End Try
        End Sub

        Public Function Nomihoshited() As Boolean
            cacheLock.EnterWriteLock()
            Try
                Return (innerSake.Equals(0))
            Finally
                cacheLock.ExitWriteLock()
            End Try
        End Function

    End Class

End Module

こんな感じ。いままでSub内部のローカル関数だったおろちの首クラスをもっと広いスコープの変数に引っ越してLISTにしておいて(Line.11)、首クラスで情報が保持できるように変更しておき(Line.83)、全ての子スレッドが終わった後で首が無事だったかどうかを判定することで子スレッドの例外を親側で検知(Line.55)って流れ。

image

このロジックの場合、全ての処理が終わらないと例外の有無が判定できないので、時間のかかる処理だとちょっとストレスがあるかもしれない。安全なアプリケーションの終了方法ではあるけれどもね。

投稿日時 : 2010年3月14日 13:48

コメント

# re: やまたのおろちが酒をのむ・リターンズその2~片桐的マルチスレッドVBプログラミング~ 2010/03/14 22:01 なちゃ
BeginInvokeしたら忘れずにEndInvokeしましょう。
スレッドで発生した例外が無視されることは基本的にはありません。


# re: やまたのおろちが酒をのむ・リターンズその2~片桐的マルチスレッドVBプログラミング~ 2010/07/31 9:56 通りすがり
>'首は別々に動けるように、マルチスレッドで宣言
>workerThread.SetApartmentState(ApartmentState.MTA)

何度指摘されても変える気はないようですが、初心者が間違った知識を得てしまわないように指摘しておきます。
MTAは通常のマルチスレッドの動作には関係有りません。
MTAにしなくてもマルチスレッドで動作します。
WaitAllを使うためならそう書いてください。
なぜ何度指摘されても頑なに間違った表現を続けるのかいまいち理解できません。

また、
>workerThread.Start()
>workerThread.Join() ただ開始して待つだけならこのスレッド不要では?
MTAにするためならMainの属性指定でやってるみたいですし。
まあ無くてもデフォルトではMTAですが。

で、それはまだしも、他のスレッドの管理をするスレッドを、
ワーカースレッドと名付けるのはちょっと違和感有ります。

あと既に指摘されてますけど、例外が補足できないのは
EndInvokeしてないからで、これは間違ったコーディングです。


# re: やまたのおろちが酒をのむ・リターンズその2~片桐的マルチスレッドVBプログラミング~ 2010/07/31 21:31 かたぎり
まいどですー

色々と、なのですが、私よりたくさん知ってらっしゃるし判ってらっしゃるみたいだから、その通りでね!
読んでる皆様方

私の脳味噌じゃ、ゆっくりだから

あと、EndInvokeしても親スレッドのCatch句で例外補足はまだできてないの。

できる人いるみたいだから、どっかにソースがあるかもね。

Post Feedback

タイトル
名前
Url:
コメント