Zpracování vláken v daném pořadí.   otázka

VB.NET

Dobrý den,
Po sériovém portu mi přicházejí z externího zařízení datové pakety. Událost SerialPort.DataReceived spouští samostatné vlákno. Loguji jak odeslané pakety, tak přijaté pakety. Každý paket (přijatý i odesílaný) se loguje do čtyř různých RichTextBoxů. Každému paketu spouštím čtyři vlákna, které provádějí operace s daným paketem a zapisují výsledná data do těch čtyř RichTextBoxů. Při větším datovém provozu na SerialPortu se mi stává, že přijaté pakety odpovědi jsou do RichTextBoxů zapsané dřív než odeslané řídící pakety (na odeslané řídící pakety reaguje zařízení a následně odešle pakety odpovědi).
Lze nějak zařídit, aby sled zapisování do RichTextBoxů vykonaných vláken byl ve stejném pořadí, v jakém se spustily? (např. pokud druhé vlákno je provedeno rychleji než první, tudíž druhé vlákno počká se zápisem do RichTextBoxu, než tam zapíše vlákno první)
Díky za návrhy řešení.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Jak zachytáváte událost při dokončení vlákna? Tam můžete kontrolovat vlastní frontu Queue a výsledky vypisovat v daném pořadí, výsledky vláken, které nejsou na řadě ukládat dokud na ně nepřijde řada.

Konkrétní návrh řešení bohužel nemám, ale snad to pomůže.

...

Vlákna? Pomocí BackgroundWorker? Thread? Task?

Zřejmě mají běžet souběžně paralerně? Nebo je nutné provést jedno po druhém či počkat na dokončení všech vláken?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Mají běžet paralelně, ale zapsat svůj výsledek do logu přesně v pořadí v jakém se spustily. Zatím nevím jak na to.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Zdravím ve spolek.

Tak řešení je poměrně jednoduché. Protože vlákna vznikají na "jednom místě" v programu, tak při jejich vzniku jim vždy přiřadím jedinečné ID a v místě, kde se vypisuje obsah paketu (který se převede do podoby člověku čitelné) do RichTextBoxu umístím

While LogovanyPaketID <> ID

Thread.Sleep(1)

End While

kde

LogovanyPaketID je ID posledního zobrazeného paketu +1

ID aktuální ID paketu který se má zobrazit.

Takhle to seřadí vlákna pěkně za sebou, v pořadí, jak se spouštěly.

Míra

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Na formu se zjeví čtyři labely, když se klikne do formuláře, tak se spustí čtyři backgroundworkeřs, které sčítají čísla. Pořadí určuje fronta. Když není ukončený job na řadě, tak se uloží výsledek na později, kdy jej bude možné v pořadí vrátit do labelu.

Imports System.ComponentModel
 
Public Class Form1
    Dim bgwks_list As List(Of Object)
    Dim q As Queue(Of Integer)
    Public Sub New()
        ' This call is required by the designer.
        InitializeComponent()
        ' Add any initialization after the InitializeComponent() call.
        For i As Integer = 0 To 3
            Dim lb As Label = New Label With {.Name = "lbl" & (i + 1), .Top = i * .Height, .Text = (i + 1).ToString}
            Controls.Add(lb)
        Next
    End Sub

    Private Sub Form1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Click
        If Not q Is Nothing Then
            Debug.Print("Waiting for results in the queue.")
            Exit Sub
        End If

        For Each c As Control In Me.Controls()
            If TypeOf c Is Label Then
                With DirectCast(c, Label)
                    .Text = ""
                    .Refresh()
                End With
            End If
        Next
        Debug.Print("To do 4 jobs.")
        bgwks_list = New List(Of Object)
        q = New Queue(Of Integer)
        For i As Integer = 0 To 3
            Dim bgwk As BackgroundWorker = New BackgroundWorker()
            bgwks_list.Add(bgwk)
            q.Enqueue(i)
            AddHandler bgwk.DoWork, AddressOf bgw_DoWork
            AddHandler bgwk.RunWorkerCompleted, AddressOf bgw_RunWorkerCompleted
            bgwk.RunWorkerAsync(i)
        Next

    End Sub

    Private Sub bgw_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs)
        Dim bgwk As BackgroundWorker = CType(sender, BackgroundWorker)
        Dim idbgwk As Integer = bgwks_list.IndexOf(bgwk)
        bgwks_list(idbgwk) = e.Result

        RemoveHandler bgwk.DoWork, AddressOf bgw_DoWork
        RemoveHandler bgwk.RunWorkerCompleted, AddressOf bgw_RunWorkerCompleted
        bgwk = Nothing

        Debug.Print("Job No:" & idbgwk & " : " & e.Result)

        Do While q.Count > 0 AndAlso Not TypeOf (bgwks_list(q.Peek)) Is BackgroundWorker
            Dim finq As Integer = q.Dequeue
            With Controls("lbl" & (finq + 1))
                .Text = bgwks_list(finq)
                .Refresh()
            End With
        Loop

        If q.Count = 0 Then
            bgwks_list.Clear()
            bgwks_list = Nothing
            q = Nothing
        End If
    End Sub

    Private Sub bgw_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs)
        Dim si As Integer = 0
        Dim ti As Integer = (5 - e.Argument) * 100

        For i As Integer = 1 To ti
            si = si + i
            Threading.Thread.CurrentThread.Sleep(e.Argument)
        Next
        e.Result = si
    End Sub
End Class
nahlásit spamnahlásit spam 0 odpovědětodpovědět

Zkoukl jsem Zdroják. Díky za inspiraci, ale možná využiji někdy v budoucnu. Zatím mi vyhovuje víc to s těmi ID. Je to podstatně jednodušší. Funkční na 100%

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Zkusil jsem to s backgroundworkery , není potřeba řešit thread safe, případně lze zobrazit info o stavu běhu pomocí ProgressChanged.

Pokud používáš Thread, lze taky zachytit jeho ukončení a nemusíš ukončené vlákno či vlákna držet ve smyčce s prodlevou, níže přidávám namátkou jeden příklad. Čekání by šlo asi realizovat i pomocí Thread.Join nebo se podívat na ThreadPool. Není známo, jak to vlastně konkrétně funguje, jak se vrací z vlákna výsledky, ale třeba to ještě začneš měřit rychlost u různých řešení a tunit na 130% ! :)

Thread Complete Event :

http://www.java2s.com/Tutorial/VB/0420Thread/Threadcompleteevent.htm

Imports System.Threading
Public Class Tester
    Shared WithEvents oSquare As SquareClass = New SquareClass()
    Public Shared Sub Main
        
        Dim t As Thread
        t = New Thread(AddressOf oSquare.CalcSquare)
        oSquare.Value = 3
        t.Start()
    
    End Sub
    Shared Sub SquareEventHandler(ByVal Square As Double) Handles oSquare.ThreadComplete
        Console.WriteLine(Square)
    End Sub
End Class

Public Class SquareClass
    Public Value As Double
    Public Square As Double

    Public Event ThreadComplete(ByVal Square As Double)

    Public Sub CalcSquare()
        Square = Value * Value
        RaiseEvent ThreadComplete(Square)
    End Sub
End Class

Velkou inspiraci nalezneš také u Task, třeba v budoucnu.

Existuje určitě přehršle řešení, třeba se tu objeví ještě další možnosti.

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.
  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback