Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Threading updating GUI 1

Status
Not open for further replies.

ifx

Technical User
Feb 26, 2002
57
GB
Hi All,

I'm building an app that generates 5 threads which all go off and fetch data from a list of web pages. I have three listviews: pages to process, pages processing and pages processed. At different points the threads need to shift one of the list view items from one list view to another. However this is where the problem occurs. From each of the threads created to fetch the pages, they raise an event on starting and another on finishing. These events are handled by the Form class. From there I invoke a delegate to update the appropriate listviews (code below).

Code:
    Private Sub TransferToProcessingHandler(ByVal itm As ListViewItem) Handles HttpDispatcher.TransferToProcessing
        If InvokeRequired Then
            Invoke(New TransferToProcessingCallback(AddressOf TransferToProcessing), New Object() {itm})

        Else
            itm = lstProcessJobs.Items(itm.Index)
            lstProcessJobs.Items(itm.Index).Remove()
            lstRunning.Items.Add(itm)
        End If
    End Sub

    Private Delegate Sub TransferToProcessingCallback(ByVal itm As ListViewItem)

    Private Sub TransferToProcessing(ByVal itm As ListViewItem)
        itm = lstProcessJobs.Items(itm.Index)
        lstProcessJobs.Items(itm.Index).Remove()
        lstRunning.Items.Add(itm)
    End Sub

Every so often an exception is thrown (IndexOutOfRange) on one or the listviews being updated. I've spent a long time trying to debug this, but have not been able to solve the problem. The list views are definitely being updated on the correct thread (i.e. the one that created them). It appears though, that when two threads raise an event at almost the same time they are overlapping on the GUI thread. eg. I've seen something similar to
Code:
itm = lstProcessJobs.Items(itm.Index)
lstProcessJobs.Items(itm.Index).Remove()
being run, but then the line of code:
Code:
itm = lstProcessJobs.Items(itm.Index)
being run again, before the first is finished. Hence of data in he listview ha changed and the exception is thrown. It just seems TransferToProcessing routine doesn't properly finish. Can I lock the object somehow? I've tried:
Code:
Synclock lstRunning
        itm = lstProcessJobs.Items(itm.Index)
        lstProcessJobs.Items(itm.Index).Remove()
        lstRunning.Items.Add(itm)
End Synclock
But it makes no difference!! Anyone seen anything like this before, or know what I've doing wrong!? It's very frustrating!! Any help would be very much appreciated!
 
Hmmm.

You have duplicate code in your else condition and your delegate callback. Can you consolidate that? Your locking with Synclock should be cleaner then.

Chip H.


____________________________________________________________________
Donate to Katrina relief:
If you want to get the best response to a question, please read FAQ222-2244 first
 
Thanks for the pointer, Chip, I've taken the duplicate code out of the else, however it's not really every fired. InvokeRequired is always true (I'm only ever calling the routine on a different thread from the UI). Unfortunately I'm still having the same problem. Man, this is a pain!
 
I think it should be something like this

Code:
[Blue]Private[/Blue] [Blue]Sub[/Blue] TransferToProcessingHandler([Blue]ByVal[/Blue] itm [Blue]As[/Blue] ListViewItem) [Blue]Handles[/Blue] HttpDispatcher.TransferToProcessing
        TrnsferToProcessing(itm)
    [Blue]End[/Blue] [Blue]Sub[/Blue]

    [Blue]Private[/Blue] Delegate [Blue]Sub[/Blue] TransferToProcessingCallback([Blue]ByVal[/Blue] itm [Blue]As[/Blue] ListViewItem)

    [Blue]Private[/Blue] [Blue]Sub[/Blue] TransferToProcessing([Blue]ByVal[/Blue] itm [Blue]As[/Blue] ListViewItem)
        [Blue]If[/Blue] InvokeRequired [Blue]Then[/Blue]
            Invoke([Blue]New[/Blue] TransferToProcessingCallback(AddressOf TransferToProcessing), [Blue]New[/Blue] Object() {itm})

        [Blue]Else[/Blue]
            itm = lstProcessJobs.Items(itm.Index)
            lstProcessJobs.Items(itm.Index).Remove()
            lstRunning.Items.Add(itm)
        [Blue]End[/Blue] [Blue]If[/Blue]
    [Blue]End[/Blue] [Blue]Sub[/Blue]

and you could throw in the synclock to be sure.

Christiaan Baes
Belgium

I just like this --> [Wiggle] [Wiggle]
 
Thanks Christiaan, that has helped tidy the code up. And while I was there I had one of those small "Ureka" moments and have managed to fix the problem! Basically my logic for adding and removing the items was totally screwed up (only three lines but it stumped me for ages!).

Basically, the code I posted would work, but when I called another delegate to transfer an item to the 'completed' listview it was still using the item index taken from the first listview (lstProcessJobs), and not the item index from the second listview (lstRunning). If that makes any sense!

Basically I'm now tagging each of the listview items with a GUID, looping throught to delete the item with that guid, and using this to create a new lv item to add to the next listview. Bit of a dumb mistake I must admit! Thank you for your help though, it got me heading in the right direction and helped me tidy the code as well! I'm well chuffed it's all working now! [bigsmile]
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top