Passing Objects Into Worker Thread Function

J

joey.powell

I am using VS2005 for a windows forms application. I need to be able to
use a worker thread function to offload some processing from the UI
thread. The worker thread will need access to a datagridview on the
form. I am using the following code to spawn the worker thread...

Thread WorkerThread = new Thread(new ThreadStart(WT_MyFunction));
WorkerThread.IsBackground = true;
WorkerThread.Start();

The problem I am having is...I cannot seem to figure out how to pass
objects into the worker thread function. For example, when I try to
use the following code...

Thread WorkerThread = new Thread(new ThreadStart(WT_MyFunction(ref
dgvMyDataGridView)));
WorkerThread.IsBackground = true;
WorkerThread.Start();

....I get the compile time error: "Method name expected".

What am I doing wrong, and how can I make this work?

JP
 
M

Marc Gravell

OK; ThreadStart does not accept parameters; in 2.0 there is
ParameterizedThreadStart, but in terms of bang-for-buck, actually captured
variables in an anonymous delegate can be more convenient, plus it is
type-safe:

Thread workerThread = new Thread((ThreadStart) delegate {
SomeFunction(someValue); // you need to define SomeFunction
});

Here "someValue" (which could be your gridview) will be "captured" (actually
moved into a seprate class instance); everything inside the braces is
actually the outer function that will be invoked on the new thread, and is
*not* evaluated as part of the assignment to workerThread. Note that
SomeFunction can be strongly typed to accept a datagridview, rather than an
object (which is what ParameterizedThreadStart takes).

However: a bigger problem here is thread affinity. You say you need access
to datagridview - well bad news. You probably can't do much with it, as you
will get illegal thread exceptions unless you do a lot of switching between
threads (via Control.Invoke / Control.BeginInvoke). You may need to do the
processing on the background thread, and then merge the results in a single
block back on the UI thread to maintain performance.

Marc
 
G

Guest

You should not access control data from anything other than the thread that
created the control. In VS2005, I suggest using the BackgroundWorker class
and have the form/control subscribe to the ProgressChanged event (if you're
not doing an atomic operation that only has a "return") or the
RunWorkerCompleted event. This class ensures that the ProgressChanged event
handler and the RunWorkerCompleted event handler are called on the thread
that created the BackgroundWorker object (which means you have to create it
on the thread that created the control in order for it to work properly).
Assuming the simplest case, you can do whatever you want to the control/form
in the RunWorkerCompleted event handler. If your handler is not static the
"this" keyword can be used to access the form/control and it's properties and
there is no need to "pass" it into the thread.
 
J

joey.powell

Okay, then. What about passing the DataGridView by value, returning it,
and then copying the returned grid over the original one. Does that
sound too crazy? I am looking for a simple way to do it.

If I use the "capture" above and do not indicate "ref", it will pass by
value, right?
 
M

Marc Gravell

I am looking for a simple way to do it.
What is the "it" that you are trying to do? "ref" is often used incorrectly
on the assumption that it is necessary to update the *contents* of an
object. It isn't. It is only necessary to reassign the *instance* of an
object. However, in either case I think I must repeat: you really, really
need to be careful of thread-affinity. It is going to bite you. Hard. As
soon as you try to pass a UI control to a different thread you are in a
world of pain. I'm just trying to save you a little frustration...

For completeness: in the more general case, from my example you can simply
define SomeFunction as accepting its parameter with the "ref" keyword, and
use "ref" in the anonymous delegate [i.e. "SomeFunction(ref someValue);"] -
this will allow someValue to be reassigned inside of SomeFunction. It really
isn't going to work with controls, though. For one thing, the "this.mygrid"
on your form is just a field; the form, however, only cares about the
instance inside "this.Controls", so *even if you fixed the thread-affinity
gotcha*, you would (if not careful) end up with a different grid on the
(visible) form (this.Controls), and in the backing "this.mygrid" field.

Personally, I doubt you really need "ref" here; try living without it and
see if it breaks ;-p

Marc
 
J

joey.powell

The whole problem here is, when I do a drag/drop on the grid, the
amount of processing (checking cell values, changing their background
colors and read-only properties, etc...) that has to occur can take
some time. Obviously I do not want the user to be staring at a "white"
datagridview that has failed to update itself. And so, I just need to
find a way to do all of this processing on a different thread. There
must be some way to make it work. Should I code a bunch of delegate
functions to change the grid background colors and read-only
properties, etc... and then use Invoke for each to call to the original
UI thread? That is not going to be fun!

What would you guys do with this?


Marc said:
I am looking for a simple way to do it.
What is the "it" that you are trying to do? "ref" is often used incorrectly
on the assumption that it is necessary to update the *contents* of an
object. It isn't. It is only necessary to reassign the *instance* of an
object. However, in either case I think I must repeat: you really, really
need to be careful of thread-affinity. It is going to bite you. Hard. As
soon as you try to pass a UI control to a different thread you are in a
world of pain. I'm just trying to save you a little frustration...

For completeness: in the more general case, from my example you can simply
define SomeFunction as accepting its parameter with the "ref" keyword, and
use "ref" in the anonymous delegate [i.e. "SomeFunction(ref someValue);"] -
this will allow someValue to be reassigned inside of SomeFunction. It really
isn't going to work with controls, though. For one thing, the "this.mygrid"
on your form is just a field; the form, however, only cares about the
instance inside "this.Controls", so *even if you fixed the thread-affinity
gotcha*, you would (if not careful) end up with a different grid on the
(visible) form (this.Controls), and in the backing "this.mygrid" field.

Personally, I doubt you really need "ref" here; try living without it and
see if it breaks ;-p

Marc
 
J

joey.powell

What about putting this in the worker thread function...

private void SomeFunctionOnUIThread()
{
Thread WorkerThread = new Thread(new
ThreadStart(WorkerThreadFunction));
WorkerThread.IsBackground = true;
WorkerThread.Start();
}

private void WorkerThreadFunction()
{
DataGridView dgv = (DataGridView)Invoke(new
GetDataGridViewDelegate(this.GetDataGridView));

//Process any "foreach", etc. on dgv. Any *changes* to be made via
Invoke calls back to UI thread
}

private DataGridView GetDataGridView() //this function is also
on UI thread
{
return(this.dgvMyDataGridView);
}

I have used technique this a couple of times before with a treeview,
but never with a datagrid view. The processing here is much more
intensive. I have never seen any warnings/failures about cross-thread
calls with this style before. What do you guys think?

The whole problem here is, when I do a drag/drop on the grid, the
amount of processing (checking cell values, changing their background
colors and read-only properties, etc...) that has to occur can take
some time. Obviously I do not want the user to be staring at a "white"
datagridview that has failed to update itself. And so, I just need to
find a way to do all of this processing on a different thread. There
must be some way to make it work. Should I code a bunch of delegate
functions to change the grid background colors and read-only
properties, etc... and then use Invoke for each to call to the original
UI thread? That is not going to be fun!

What would you guys do with this?


Marc said:
I am looking for a simple way to do it.
What is the "it" that you are trying to do? "ref" is often used incorrectly
on the assumption that it is necessary to update the *contents* of an
object. It isn't. It is only necessary to reassign the *instance* of an
object. However, in either case I think I must repeat: you really, really
need to be careful of thread-affinity. It is going to bite you. Hard. As
soon as you try to pass a UI control to a different thread you are in a
world of pain. I'm just trying to save you a little frustration...

For completeness: in the more general case, from my example you can simply
define SomeFunction as accepting its parameter with the "ref" keyword, and
use "ref" in the anonymous delegate [i.e. "SomeFunction(ref someValue);"] -
this will allow someValue to be reassigned inside of SomeFunction. It really
isn't going to work with controls, though. For one thing, the "this.mygrid"
on your form is just a field; the form, however, only cares about the
instance inside "this.Controls", so *even if you fixed the thread-affinity
gotcha*, you would (if not careful) end up with a different grid on the
(visible) form (this.Controls), and in the backing "this.mygrid" field.

Personally, I doubt you really need "ref" here; try living without it and
see if it breaks ;-p

Marc
 
J

joey.powell

I would love to figure out how to do the processing on the background
thread. Trouble is, all of the "processing" involves iterating over the
contents of the datagridview rows.

I can plant plenty of code in the worker thread that will change the
various things in the rows via Invoke calls to the UI thread, but a
real problem is how do I get to where I can actually iterate over them
on the worker thread (foreach, etc...) - that is necessary. The way I
see it, the iteration must be done on the UI thread, period. If that is
so then I will never get rid of my screen-not-updating problem.

I have tried creating a List<DataGridViewRow>, and I have also tried
creating a "DataGridViewRow[] Rowz;" and then using
dgvMyDataGridView.Rows.CopyTo(Rowz). But when they get passed into the
worker thread and are evaluated, cross-thread exceptions are thrown,
just the same. It looks to me like I "copied" the rows, but at runtime
its like I tried to pass the actual rows???

There must be some way to tackle this. What would you do if you had a
datagridview with LOTS of processing that needed to be done, and all of
the processing involved iterating over the rows? Does anyone have an
idea?
 
B

Bruce Wood

I would love to figure out how to do the processing on the background
thread. Trouble is, all of the "processing" involves iterating over the
contents of the datagridview rows.

I can plant plenty of code in the worker thread that will change the
various things in the rows via Invoke calls to the UI thread, but a
real problem is how do I get to where I can actually iterate over them
on the worker thread (foreach, etc...) - that is necessary. The way I
see it, the iteration must be done on the UI thread, period. If that is
so then I will never get rid of my screen-not-updating problem.

I have tried creating a List<DataGridViewRow>, and I have also tried
creating a "DataGridViewRow[] Rowz;" and then using
dgvMyDataGridView.Rows.CopyTo(Rowz). But when they get passed into the
worker thread and are evaluated, cross-thread exceptions are thrown,
just the same. It looks to me like I "copied" the rows, but at runtime
its like I tried to pass the actual rows???

There must be some way to tackle this. What would you do if you had a
datagridview with LOTS of processing that needed to be done, and all of
the processing involved iterating over the rows? Does anyone have an
idea?

I'm no expert, but what I do is gather the information I need to do
that "lots of processing" and put it in some structure completely
unrelated to the UI controls. I then launch the worker thread and have
it put its results in another structure unrelated to the UI controls.
Finally, I return control to the UI thread and copy the results into
the UI controls.

It seems like a lot of extra work, but updating the UI is usually
pretty quick. I've found a few circumstances in which it is noticeably
slow (usually ListViews with large numbers of items) but in general I
can break up the code so that trips back to the DB, for example, are in
the background thread.

In other words, it looks sort of like this:

private void SomeEvent(object sender, System.EventArgs e)
{
// Get info from UI controls and place it in Hashtables,
ArrayLists, whatever
// Launch the background thread
}

private void SomeEventThreadJob()
{
// Do the slow stuff and put the results in Hashtables, ArrayLists,
whatever
// Invoke back to the UI thread
}

private void ContinueSomeEvent()
{
// Copy results from output structures of SomeEventThreadJob into
UI controls
}

The only one that has me stumped is a problem with creating large
numbers of ListViewItems, which seems to take a noticeable amount of
time, and can't be relegated to a background thread.
 
J

joey.powell

I built a couple of custom classes to hold the data. The first holds a
snapshot of the relevant data in any of the datagridview's rows. The
second was a collection that held X numbers of these class objects.

Next I created the custom collection and then iterated over each
datagridviewrow in the datagridview, each time creating and populating
a class and then adding it to the collection.

Then I called the worker thread and passed in the collection.

The worker thread then processed the data and returned the updated
collection to the UI thread.

Then, back on the UI thread I parsed each object in the returned
collection and used it to update the grid again.

It sounded good at first. BUT...

The time that it takes to iterate over the datagridview rows (must be
done before I can get on the worker thread) takes so long that the
screen still does not update. The cursor remains Cursors.Default, even
though I put the line this.Cursor = Cursors.WaitCursor several lines
earlier. The users are (understandably) getting confused. The problem
is being caused by the large amount of data in the grid (1000+ rows).

I am almost convinced that there is no way to fix this. Maybe this is
just a problem that we have to live with if we are going to use lots of
data in a datagridview.

If anyone else has any ideas please post them.

Thanks in advance,
JP


Bruce said:
I would love to figure out how to do the processing on the background
thread. Trouble is, all of the "processing" involves iterating over the
contents of the datagridview rows.

I can plant plenty of code in the worker thread that will change the
various things in the rows via Invoke calls to the UI thread, but a
real problem is how do I get to where I can actually iterate over them
on the worker thread (foreach, etc...) - that is necessary. The way I
see it, the iteration must be done on the UI thread, period. If that is
so then I will never get rid of my screen-not-updating problem.

I have tried creating a List<DataGridViewRow>, and I have also tried
creating a "DataGridViewRow[] Rowz;" and then using
dgvMyDataGridView.Rows.CopyTo(Rowz). But when they get passed into the
worker thread and are evaluated, cross-thread exceptions are thrown,
just the same. It looks to me like I "copied" the rows, but at runtime
its like I tried to pass the actual rows???

There must be some way to tackle this. What would you do if you had a
datagridview with LOTS of processing that needed to be done, and all of
the processing involved iterating over the rows? Does anyone have an
idea?

I'm no expert, but what I do is gather the information I need to do
that "lots of processing" and put it in some structure completely
unrelated to the UI controls. I then launch the worker thread and have
it put its results in another structure unrelated to the UI controls.
Finally, I return control to the UI thread and copy the results into
the UI controls.

It seems like a lot of extra work, but updating the UI is usually
pretty quick. I've found a few circumstances in which it is noticeably
slow (usually ListViews with large numbers of items) but in general I
can break up the code so that trips back to the DB, for example, are in
the background thread.

In other words, it looks sort of like this:

private void SomeEvent(object sender, System.EventArgs e)
{
// Get info from UI controls and place it in Hashtables,
ArrayLists, whatever
// Launch the background thread
}

private void SomeEventThreadJob()
{
// Do the slow stuff and put the results in Hashtables, ArrayLists,
whatever
// Invoke back to the UI thread
}

private void ContinueSomeEvent()
{
// Copy results from output structures of SomeEventThreadJob into
UI controls
}

The only one that has me stumped is a problem with creating large
numbers of ListViewItems, which seems to take a noticeable amount of
time, and can't be relegated to a background thread.
 
B

Bruce Wood

I built a couple of custom classes to hold the data. The first holds a
snapshot of the relevant data in any of the datagridview's rows. The
second was a collection that held X numbers of these class objects.

Next I created the custom collection and then iterated over each
datagridviewrow in the datagridview, each time creating and populating
a class and then adding it to the collection.

Then I called the worker thread and passed in the collection.

The worker thread then processed the data and returned the updated
collection to the UI thread.

Then, back on the UI thread I parsed each object in the returned
collection and used it to update the grid again.

It sounded good at first. BUT...

The time that it takes to iterate over the datagridview rows (must be
done before I can get on the worker thread) takes so long that the
screen still does not update. The cursor remains Cursors.Default, even
though I put the line this.Cursor = Cursors.WaitCursor several lines
earlier. The users are (understandably) getting confused. The problem
is being caused by the large amount of data in the grid (1000+ rows).

I am almost convinced that there is no way to fix this. Maybe this is
just a problem that we have to live with if we are going to use lots of
data in a datagridview.

If anyone else has any ideas please post them.

I don't use data grids myself, so take the following with a grain of
salt.

It sounds to me as though you're using the DataGridView as your data
store. Perhaps you should move to a model in which the data is stored
elsewhere (for example in a DataTable) and then displayed in the UI. I
believe that you can use data bindings to bind an ADO.NET table or
tables to a data grid view.

This would save you the step of having to extract the data from the UI
in order to work with it in a background thread. You would already have
the data in a DataTable object, so you could legally read that in a
background thread; your only problem would be how to update the UI with
changes you make to the DataTable.

The only solutions I can think of right now are:

1. Wire up the backing store (DataTable) to the UI (DataGridView) in
such a way that events updating the UI always go through Invoke if
necessary. This may be part of how DataBindings work... I'm not sure.

2. Store a record of the changes needed in some data structure (as
you're doing now) and do the update when you're back in the UI thread.

Either way, what you save is the work of extracting the data to analyze
up front.
 
J

joey.powell

That's good info - I'll try to use it. I already have a dataset with a
datatable that stores the data. The grid is bound to it. One problem I
am going to face, though, is that I am having to check characteristics
of the datagrid itself (I was emulating that in my custom classes) like
readonly, backcolor, etc.... And so, I still have to iterate over the
rows to get at that before calling the worker thread.

Thanks for the feedback!
 
B

Bruce Wood

That's good info - I'll try to use it. I already have a dataset with a
datatable that stores the data. The grid is bound to it. One problem I
am going to face, though, is that I am having to check characteristics
of the datagrid itself (I was emulating that in my custom classes) like
readonly, backcolor, etc.... And so, I still have to iterate over the
rows to get at that before calling the worker thread.

Maybe you need to store that information, or the information needed to
derive those colours and settings, in a backing store, and then
(effectively) bind that to the grid view. It's pretty easy to set up
classes that raise events whenever their properties are changed, and
write simple bindings that listen to an instance of that class and
update things like background colour accordingly.

I do that in my software: I tried playing with MS's data bindings (.NET
1.1) and found them confusing and overly complicated, so I wrote my
own. It isn't terribly complicated if you know Reflection.

The added bonus is that if you write your own "bindings" then you can
make them smart enough to check InvokeRequired on the data grid and use
Invoke accordingly, so that you don't have cross-thread update problems
with the UI. That way you eliminate both the "suck the settings out of
the data grid" phase and the "update the entire data grid" phase.
 
V

Vincent

Why do you even have more than a thousand rows in a datagrid?

That is too many for a user to view at one time anyway. Shouldn't you
change it so that the grid only shows the data that the user actually
NEEDS to see at any one time?

Vincent.

I built a couple of custom classes to hold the data. The first holds a
snapshot of the relevant data in any of the datagridview's rows. The
second was a collection that held X numbers of these class objects.

Next I created the custom collection and then iterated over each
datagridviewrow in the datagridview, each time creating and populating
a class and then adding it to the collection.

Then I called the worker thread and passed in the collection.

The worker thread then processed the data and returned the updated
collection to the UI thread.

Then, back on the UI thread I parsed each object in the returned
collection and used it to update the grid again.

It sounded good at first. BUT...

The time that it takes to iterate over the datagridview rows (must be
done before I can get on the worker thread) takes so long that the
screen still does not update. The cursor remains Cursors.Default, even
though I put the line this.Cursor = Cursors.WaitCursor several lines
earlier. The users are (understandably) getting confused. The problem
is being caused by the large amount of data in the grid (1000+ rows).

I am almost convinced that there is no way to fix this. Maybe this is
just a problem that we have to live with if we are going to use lots of
data in a datagridview.

If anyone else has any ideas please post them.

Thanks in advance,
JP


Bruce said:
I would love to figure out how to do the processing on the background
thread. Trouble is, all of the "processing" involves iterating over the
contents of the datagridview rows.

I can plant plenty of code in the worker thread that will change the
various things in the rows via Invoke calls to the UI thread, but a
real problem is how do I get to where I can actually iterate over them
on the worker thread (foreach, etc...) - that is necessary. The way I
see it, the iteration must be done on the UI thread, period. If that is
so then I will never get rid of my screen-not-updating problem.

I have tried creating a List<DataGridViewRow>, and I have also tried
creating a "DataGridViewRow[] Rowz;" and then using
dgvMyDataGridView.Rows.CopyTo(Rowz). But when they get passed into the
worker thread and are evaluated, cross-thread exceptions are thrown,
just the same. It looks to me like I "copied" the rows, but at runtime
its like I tried to pass the actual rows???

There must be some way to tackle this. What would you do if you had a
datagridview with LOTS of processing that needed to be done, and all of
the processing involved iterating over the rows? Does anyone have an
idea?

I'm no expert, but what I do is gather the information I need to do
that "lots of processing" and put it in some structure completely
unrelated to the UI controls. I then launch the worker thread and have
it put its results in another structure unrelated to the UI controls.
Finally, I return control to the UI thread and copy the results into
the UI controls.

It seems like a lot of extra work, but updating the UI is usually
pretty quick. I've found a few circumstances in which it is noticeably
slow (usually ListViews with large numbers of items) but in general I
can break up the code so that trips back to the DB, for example, are in
the background thread.

In other words, it looks sort of like this:

private void SomeEvent(object sender, System.EventArgs e)
{
// Get info from UI controls and place it in Hashtables,
ArrayLists, whatever
// Launch the background thread
}

private void SomeEventThreadJob()
{
// Do the slow stuff and put the results in Hashtables, ArrayLists,
whatever
// Invoke back to the UI thread
}

private void ContinueSomeEvent()
{
// Copy results from output structures of SomeEventThreadJob into
UI controls
}

The only one that has me stumped is a problem with creating large
numbers of ListViewItems, which seems to take a noticeable amount of
time, and can't be relegated to a background thread.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Top