If this is your first visit, be sure to
check out the FAQ by clicking the
link above. You may have to register or Login
before you can post: click the register link above to proceed. To start viewing messages,
select the forum that you want to visit from the selection below.

Getting an object from a different thread

Ok, I've done some googling, searched these forums, but I couldn't find the answer to my problem. So here it is:
I've got a function in a class that creates a new thread which creates an XMLDocument object and downloads data into it. After it is finished it should somehow pass on that XMLDocument to the main thread (that runs the class, so to speak) so I can get data from it.
So I don't have to access any data while the thread is running, if that makes a difference (and I think it does, from what I've seen so far).

So far the only code I've got working is the code without sharing the XMLDocument when finished, and it is this:

using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Net;
using System.IO;
using System.Windows.Forms;
using System.Threading;
using System.Drawing;

Re: Getting an object from a different thread

Quick fix (I can't edit my post (?)):
System.Windows.Forms was only there to be able to show a messagebox every now and then, it's not there for any serious purpose and will be removed again, so this is not some kind of control or form, might that not have been clear already.
System.Windows.Drawing is there because there's some image-processing to be done once the XmlDocument is downloaded.

Re: Getting an object from a different thread

Well Arjay suggested what I was about to explain. Since its a rather complicated solution and hard to explain unless you already know how to work with events I made a little program to show you what hes talking about.

Hope this example helps, i tried to document it the best I could so you would understand whats going on. It works for me, basically it loads an xml document from a thread and populates a ListBox from the nodes it read from the xml document.

Re: Getting an object from a different thread

The thing is: that's not really what I want to do.
I have 2 threads within the rss-class, one 'main' thread that's running the rss-class, and my other thread that downloads the XML. When the XML-downloading is done, I want the main thread (that's running the rss-class) to have access to the XmlDocument to get some values from it. The XmlDocument does not need to be shared between classes, only between the two threads within that class.

Re: Getting an object from a different thread

If you already have a thread that the rss-class is running in then why do you need a second thread to download the xml? You're not blocking the UI or anything. You can't just return a object from a thread when the thread is done. You have to fire some event letting the other thread know that the resources are available to use. As soon as the thread that downloads the xml starts, the thread calling it continues. It can't wait for the thread (if it did you wouldn't be using a thread) to return some value.

Re: Getting an object from a different thread

Monalin is right - why use a thread in the first place. If you have thread A blocking until thread B has loaded the xml document, why not just load the xml document in thread A ?

Code:

The XmlDocument does not need to be shared between classes, only between the two threads within that class.

I think you're confused. A 'thread' is not a member of any class - it is an execution path. As such it can (and does) go everywhere and anywhere, access any data it pleases just as the main thread can.

Threads are not related to classes, it's just in .NET you have a thread class to manage them and enable you to refer to them.

Re: Getting an object from a different thread

Monalin is right - why use a thread in the first place. If you have thread A blocking until thread B has loaded the xml document, why not just load the xml document in thread A ?

Code:

The XmlDocument does not need to be shared between classes, only between the two threads within that class.

I think you're confused. A 'thread' is not a member of any class - it is an execution path. As such it can (and does) go everywhere and anywhere, access any data it pleases just as the main thread can.

Threads are not related to classes, it's just in .NET you have a thread class to manage them and enable you to refer to them.

Darwen.

Yeah, some poor choice of words there on my behalf. The 'main' thread runs both the UI and loads the rss-class. Then the rss-class spawns another thread to download the XML. When that is done, the thread that runs both the GUI and the rss-class should be notified and presented somehow with the XmlDocument so it can get some data from the Xml to pass on to the GUI.
I don't want to invoke controls or anything like that, I want to keep it all separated so it's usable in lots of different projects...

Re: Getting an object from a different thread

Originally Posted by graey

Yeah, some poor choice of words there on my behalf. The 'main' thread runs both the UI and loads the rss-class. Then the rss-class spawns another thread to download the XML. When that is done, the thread that runs both the GUI and the rss-class should be notified and presented somehow with the XmlDocument so it can get some data from the Xml to pass on to the GUI.
I don't want to invoke controls or anything like that, I want to keep it all separated so it's usable in lots of different projects...

By modifying my example code i attached slightly you can do just that. I believe my code does what you're looking for. You just need to tweak it a little to work with your system. The same principles apply.

"Then the rss-class spawns another thread to download the XML" - DOES IT

" When that is done, the thread that runs both the GUI and the rss-class should be notified" - DOES IT (via an event)

" and presented somehow with the XmlDocument so it can get some data from the Xml to pass on to the GUI" - DOES IT

If you don't understand the code i attached please ask questions. I think using events and callback functions are the only way you can accomplish what you're trying to do. Heres the slightly modified code, just change Form1.cs to this...

Code:

public XmlDocument xml = new XmlDocument();
public Form1()
{
InitializeComponent();
}
private void populateNames_Click(object sender, EventArgs e)
{
// Create a new instance of the names class
Names myNames = new Names();
// Register the event to fire when we have the xml data
myNames.PopulateNamesComplete += new PopulateNamesCompleteEventHandler(populateNames_Complete);
// Start the new thread
myNames.LoadNames();
}
private void populateNames_Complete(Object sender, PopulateNamesCompleteEventArgs e)
{
// When this thread gets called its still running in the other thread so you can't
// access any of the UI controls on the form unless you do so through a delegate funciton
// if you just wanted to assign a variable you can still do that without invoking
// a delegate method.
xml = e.Xml;
// Show the xml just so you know it completed.
MessageBox.Show(xml.OuterXml);
}

Re: Getting an object from a different thread

Ah, pass it as part of the event arguments, hadn't noticed that yet, sorry. That indeed seems like it will work exactly like I want it to! I'll change it so that the rss-class is listening to the event itself and then it should work exactly like I want. Thanks!

Re: Getting an object from a different thread

Originally Posted by graey

Ah, pass it as part of the event arguments, hadn't noticed that yet, sorry. That indeed seems like it will work exactly like I want it to! I'll change it so that the rss-class is listening to the event itself and then it should work exactly like I want. Thanks!

Yup, thats the cool thing about events. You can pass whatever variables you want through the EventArgs. You can even change the PopulateNamesCompleteEventArgs class so that it can send more than XmlDocuments just by adding more properties.

Re: Getting an object from a different thread

Originally Posted by monalin

Yup, thats the cool thing about events. You can pass whatever variables you want through the EventArgs. You can even change the PopulateNamesCompleteEventArgs class so that it can send more than XmlDocuments just by adding more properties.

I see. It's working now, but there's one minor thing I still don't like.
Right now the rss-class creates a thread that downloads xml, processes the xml itself so that a public property of the rss-class is filled. Then it fires an event, and that's where the main thread (that also runs the GUI) picks it up. Right now I need delegates and invokes in my main thread, while I'd rather see 'normal' calls there with the delegates only in the class that actually does some (multi)threading. I've been tinkering with the code but I can't get it to work that way, yet I've seen libraries that do this (though sadly enough only binaries, no source).
(I want it to work that way because it's a class library that should be easy to use for novice programmers, the threading-stuff safely hidden from the users of the class lib).

Re: Getting an object from a different thread

Another way to approach this is to pass in the WindowsFormsSynchronizationContext to the Names class, then use the Post method.

Code:

privatevoidpopulateNames_Click(object sender, EventArgs e)
{ // Create a new instance of the names class
// Pass in the current UI synchronization context
NamesmyNames = Names.Create(WindowsFormsSynchronizationContext.Current ); // Register the event to fire when we have the xml data
myNames.PopulateNamesComplete +=newPopulateNamesCompleteEventHandler(populateNames_Complete); // Start the new thread
myNames.LoadNames();}

The Names class is modified to use the SynchronizationContext

Code:

Code:

// This is the entry point for the thread we use to load the names
privatevoid StartLoadNames()
{
XmlDocument xml = newXmlDocument();
// You can load the xml document from a url here instead. I just did this cause its simple.
xml.LoadXml("<names><name>Brian</name><name>Jerry</name><name>Andrew</name></names>");
// Use the passed in SynchronizationContext to post
// to the UI context
objectstate =newobject[] { xml }; _context.Post(OnPostToUIContext, state);
}
// This runs in the UI thread context
// It just fires the PopulateNamesCompleted event
privatevoid OnPostToUIContext(object state)
{
XmlDocumentxml = ((XmlDocument)((object[])state)[0]); // Fire the event.
OnPopulateNamesComplete(newPopulateNamesCompleteEventArgs(xml));
}

This is a very quick representation, so the code can definitely be cleaned up.