Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: VB.NET: [vb.net] Threading ongelma

Sivun loppuun

novice [04.09.2009 14:33:56]

#

Moikka kaikille.
Nyt kun kesä alkaa oleen ohi niin pääseepi taas ohjelmointiharrastuksen ja toisinaan tyhmien kysymysten pariin.

Aloin kokeileen ohjelman "säikeistystä" ja aika helposti sain luotua yksinkertaisen säieohjelman.
Nyt haluaisin säieohjelman lisäävän textboxiin ohjelman kulusta kertovia kommentteja, mutta nämä kommentit eivät päivity textboxiin. Säieohjelma kyllä rullaa kokoajan, mutta textboxin päivitystä ei tapahdu.
Kun sama säieohjelma kutsutaan tavallisena aliohjelmana, niin kaikki menee hienosti.

Säieohjelmasta löytyy .refresh ja .doevents, mutta ei niistäkään ole apua.
Mikäs tässä nyt mättää?

Grez [04.09.2009 14:57:42]

#

Onko toi taas niitä salaisia ohjelmia, joiden lähdekoodeja ei voi näyttää muille?

Vaikea koodia näkemättä sanoa. Ilmeisesti kuitenkin käytät delegaattia, koska muutenhan se ohjelma kai kaatuisi kokonaan siinä vaiheessa kun yrität päivittää.

novice [05.09.2009 13:24:43]

#

Ei se salainen ole, en vaan jaksanut liittää sitä viestiin. Ajattelin, että jospa joku osaisi auttaa ilman koodin näkemistä.
Koodi on työkoneella joten en voi sitä nytkään tähän liittää.
Mitä tarkoitat delegaatilla?

Grez [05.09.2009 16:35:21]

#

lainaus:

Mitä tarkoitat delegaatilla?

No kun toisen threadin kontrolleja ei voi käyttää suoraan, niin delegaatin avullahan se onnistuu.

Eli jos kerran et ole delegaattia käyttänyt, niin ihmettelen kyllä että et saa poikkeusta tuosta kun yrität muuttaa sitä textboxin arvoa. Olisiko sulla siellä joku "virheenkäsittely" try-catch'em'all rakenne, joka ei käsittele mitään vaan syö vaan poikkeukset.

lainaus:

Ei se salainen ole, en vaan jaksanut liittää sitä viestiin. Ajattelin, että jospa joku osaisi auttaa ilman koodin näkemistä.

Siis voihan sitä arvailla. Ainoa vaan, että jos arvaan mitä olet tehnyt (alla), niin sen pitäisi heittää poikkeus, ellei niitä ole järjenvastaisesti "syöty".

Jos ihan vaan lyön formille Button1 painikkeen ja TextBox1 tekstilaatikon ja teen seuraavan koodin:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim MunSäie As New Threading.Thread(AddressOf SäieOhjelma)
    MunSäie.Start()
End Sub
Private Sub SäieOhjelma()
    TextBox1.Text = "Testi"  'Tästä tulee poikkeus
End Sub

Eli kun painaa nappia niin tuolta merkityltä riviltä tulee seuraava poikkeus:
System.InvalidOperationException was unhandled
Message="Cross-thread operation not valid: Control 'TextBox1' accessed from a thread other than the thread it was created on."

Eli tuossahan se sanotaan selvällä englannilla, että yritettiin muuttaa controllin arvoa väärästä säikeestä. Jos tuo poikkeus vaan "syödään" niin lopputuloksena tekstiboksi ei päivity.

Tässä vielä sama delegaatilla tehtynä (toimivana)

Public Class Form1
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim MunSäie As New Threading.Thread(AddressOf SäieOhjelma)
        MunSäie.Start()
    End Sub
    'Textboxin päivittävä aliohjelma
    Private Sub AsetaTextBox1Text(ByVal UusiArvo As String)
        TextBox1.Text = UusiArvo
    End Sub
    'Delegaattimääritys edellisen aliohjelman käyttämiseen Invokella
    Private Delegate Sub dlgAsetaTextBox1Text(ByVal UusiArvo As String)
    Private Sub SäieOhjelma()
        'Tehdään delegaatti
        Dim dlg As New dlgAsetaTextBox1Text(AddressOf AsetaTextBox1Text)
        'Pyydetään Textbox1:n säiettä käynnistämään delegaatti
        TextBox1.Invoke(dlg, "Testi")
    End Sub
End Class

novice [05.09.2009 18:12:19]

#

Kiitos Grez.
Uskon, että se puuttuva "lenkki" oli juuri tuo delegointi. Sitä ei minulla koodissa ole. Ja poikkeus on syöty tällä:

Control.CheckForIllegalCrossThreadCalls = false

Pääsen vasta maanantaina testaileen koodia.

novice [08.09.2009 21:48:41]

#

Ihan vaan selvennyksen vuoksi haluaisin vielä kysäistä, että:
Entäpä jos haluaisin laittaa kaiken muun paitsi tuon säieohjelman moduliin (ihan vaan selkeyden vuoksi) alla olevan tyyliin, niin joudunko tosiaan aina Kommentoi-ohjelmaa kutsuttaessa kertomaan myös textboxin nimen (joka on tässä tapauksessa aina sama)? Vai onko jokin muu keino?

Public Class Form1


    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim MunSäie As New Threading.Thread(AddressOf SäieOhjelma)
        MunSäie.Start()
    End Sub

    Private Sub SäieOhjelma()
        Kommentoi(TextBox1, "Plaaplaaplaa, plaaplaaapla")
    End Sub

End Class

Moduliin:

Module Module1
    Private Delegate Sub dlgAsetaTextBox1Text(ByVal UusiArvo As String)


    Public Sub Kommentoi(ByVal TB As TextBox, ByVal teksti As String)
        'Tehdään delegaatti
        Dim dlg As New dlgAsetaTextBox1Text(AddressOf AsetaTextBox1Text)
        'Pyydetään Textbox1:n säiettä käynnistämään delegaatti
        TB.Invoke(dlg, teksti)
    End Sub

    'Textboxin päivittävä aliohjelma
    Private Sub AsetaTextBox1Text(ByVal UusiArvo As String)
        Form1.TextBox1.Text = UusiArvo
    End Sub


End Module

novice [09.09.2009 17:01:25]

#

Haa! Nyt tajusin ton systeemin mikä siinä on ideana. Asia luultavasti loppuunkäsitelty!

Metabolix [09.09.2009 17:05:14]

#

Voit tosiaan käyttää ilmaisua "Form1.TextBox1", kuten olet toisessakin kohti jo tehnyt.

novice [10.09.2009 08:01:53]

#

Ei muuten onnistu Metabolix.

Metabolix [10.09.2009 11:07:51]

#

Miksei? :o Onko se ehkä Private?

novice [10.09.2009 12:26:07]

#

Tuleepi seuraava virheilmoitus:
Invoke or BeginInvoke cannot be called on a control until the window handle has been created.

Tiedät varmaan itse peremmin mitä tuo tarkoittaa.

Grez [10.09.2009 12:39:27]

#

No eikös tuossa sanota. Yritit käyttää invokea ennen kuin sitä toista ikkunaa oli luotu.

novice [10.09.2009 13:06:56]

#

Mitä toista ikkunaa?

tämä rivi siis aiheuttaa virheen:

Form1.Textbox1.Invoke(dlg, teksti)

Metabolix [10.09.2009 13:13:50]

#

Ja väitätkö nyt, että muuten aivan sama koodi toimii, jos funktiot ovatkin Form1:n sisällä ja/tai annat boksin funktiolle parametrina?

novice [10.09.2009 13:17:40]

#

Juuri näin.

Metabolix [10.09.2009 14:10:31]

#

Oletko aivan varma, ettet yritä muuttaa tekstiä, ennen kuin ikkuna ja boksi on luotu?

Testaapa tuosta vielä. Tämä ainakin minulla toimii aivan hyvin riippumatta siitä, mitkä osat ovat Testi-luokassa ja mitkä m1-moduulissa, eikä sillä mitään merkitystä pitäisi ollakaan. Tässä säie ajetaan, kun formia klikataan.

Imports System.Windows.Forms
Imports System.Drawing
Imports System.Threading

Public Class Testi
    Inherits Form
    Public tb As TextBox
    Public Sub New()
        Me.Text = "Testi"
        Me.Size = new Size(200, 200)
        Me.CenterToScreen()

        Me.tb = new TextBox()
        Me.tb.Parent = Me
        Me.tb.Location = new Point(0, 0)
        Me.tb.Size = new Size(100, 24)
        Me.Controls.Add(Me.tb)

        AddHandler Me.Click, AddressOf Me.OnClick
    End Sub

    Private Sub OnClick(btn As Object, e As EventArgs)
        Dim t As Thread
        t = new Thread(AddressOf UpdateBox_Thread)
        t.Start()
    End Sub

    Private Sub UpdateBox_Thread
        Dim i As Integer
        For i = 1 To 500
            m1.UpdateBox_Caller(i)
        Next
    End Sub
End Class

Module m1
    Dim form As Testi
    Public Sub UpdateBox_Caller(i As Integer)
        form.tb.Invoke(new UpdateBox_Delegate(AddressOf UpdateBox_Real), i.ToString())
    End Sub

    Public Sub UpdateBox_Real(str As String)
        form.tb.Text = str
    End Sub

    Public Delegate Sub UpdateBox_Delegate(s As String)

    Public Sub Main
        form = new Testi()
        Application.Run(form)
    End Sub
End Module

Edit: Antti hoi, VB-kooditagi syö edelleenkin tabulaattorit!

Grez [10.09.2009 14:37:32]

#

novice kirjoitti:

Mitä toista ikkunaa?

No sitä Form1 ikkunaa.

novice [10.09.2009 17:51:57]

#

Koodiasi piti hieman muokata jotta sain editorin hyväksymään sen (miksiköhän?):

Imports System.Windows.Forms
Imports System.Drawing
Imports System.Threading
Public Class Testi
    Inherits Form
    Public tb As TextBox

    Private Sub Testi_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Me.Text = "Testi"
        Me.Size = New Size(200, 200)
        Me.CenterToScreen()

        Me.tb = New TextBox()
        Me.tb.Parent = Me
        Me.tb.Location = New Point(0, 0)
        Me.tb.Size = New Size(100, 24)
        Me.Controls.Add(Me.tb)

        AddHandler Me.Click, AddressOf Me.Testi_Click
    End Sub

    Private Sub Testi_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Click
        Dim t As Thread
        t = New Thread(AddressOf UpdateBox_Thread)
        t.Start()
    End Sub

    Private Sub UpdateBox_Thread()
        Dim i As Integer
        For i = 1 To 500
            m1.UpdateBox_Caller(i)
        Next
    End Sub
End Class

Module m1
    Dim form As Testi
    Public Sub UpdateBox_Caller(ByVal i As Integer)
        form.tb.Invoke(New UpdateBox_Delegate(AddressOf UpdateBox_Real), i.ToString())
    End Sub

    Public Sub UpdateBox_Real(ByVal str As String)
        form.tb.Text = str
    End Sub

    Public Delegate Sub UpdateBox_Delegate(ByVal s As String)

    Public Sub Main()
        form = New Testi()
        Application.Run(form)
    End Sub
End Module

Ja nyt pukkaa virhettä:
Object reference not set to an instance of an object.
riviltä:

form.tb.Invoke(New UpdateBox_Delegate(AddressOf UpdateBox_Real), i.ToString())

Edit: Ohjelman ajo menee toisinaan läpi ilman virheitä????? (kestää vaan n.10sek), mutta boxissa ei ole kertaakaan näkynyt merkkiäkään.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta