using delegates for multithreading

  • Thread starter Thread starter VM
  • Start date Start date
V

VM

Hi,
In my Win app, I'm using delegates because the process that'll be running is
time-consuming and it requires parameters. The only problem I'm having is
that the lines following the BeginInvoke (lines that depend on the outcome
of the method) are being executed while the lengthy process is being
executed. Would it be possible to "tell" those following lines of code not
to execute while the BeginInvoke method is running and run after the
BeginInvoke has finished executing ?

loadAuditBrowseHandler thisLoadAuditBrowseHandler = new
loadAuditBrowseHandler(this.Browse);
thisLoadAuditBrowseHandler.BeginInvoke(null,null);

// many more lines of code that cannot be executed while the BeginInvoke is
running
 
VM said:
In my Win app, I'm using delegates because the process that'll be running is
time-consuming and it requires parameters. The only problem I'm having is
that the lines following the BeginInvoke (lines that depend on the outcome
of the method) are being executed while the lengthy process is being
executed. Would it be possible to "tell" those following lines of code not
to execute while the BeginInvoke method is running and run after the
BeginInvoke has finished executing ?

loadAuditBrowseHandler thisLoadAuditBrowseHandler = new
loadAuditBrowseHandler(this.Browse);
thisLoadAuditBrowseHandler.BeginInvoke(null,null);

// many more lines of code that cannot be executed while the BeginInvoke is
running

If those lines of code can't be executed until the delegate has
finished, why are you invoking it asynchronously in the first place?

If this is in the UI thread, you shouldn't have the second portion of
code in the same method - instead, provide a callback delegate which is
called when the delegate has finished, and from that delegate call
Control.Invoke or Control.BeginInvoke to "get back" to the UI thread
and deal with the results.
 
Since it's an MDI application, the user can use any of the other separate
windows while this is running. This is running within the UI thread.

How would you create a callback delegate that'll get me back to the main UI
thread?
This is the basic structure of my window:

private delegate void loadAuditBrowseHandler();
private void btn_filter_Click(object sender, System.EventArgs e)
{
loadAuditBrowseHandler thisLoadAuditBrowseHandler = new
loadAuditBrowseHandler(this.Browse);
thisLoadAuditBrowseHandler.BeginInvoke(null,null);
//Code that filters the data in the datatable and attaches the table to
the grid. Also does other misc. stuff
}

private void Browse()
{
// Code that fills a private member datatable with several hundred rows -
may take several seconds
/* During this process, I don't want the application to look like it
froze */
}

Thanks again.
 
VM said:
Since it's an MDI application, the user can use any of the other separate
windows while this is running. This is running within the UI thread.

How would you create a callback delegate that'll get me back to the main UI
thread?

Just use something with the same signature as AsyncCallback, call
EndInvoke on the IAsyncResult which is passed to you, and then call
Invoke or BeginInvoke on another delegate to do whatever you need to do
in the UI.
This is the basic structure of my window:

private delegate void loadAuditBrowseHandler();
private void btn_filter_Click(object sender, System.EventArgs e)
{
loadAuditBrowseHandler thisLoadAuditBrowseHandler = new
loadAuditBrowseHandler(this.Browse);
thisLoadAuditBrowseHandler.BeginInvoke(null,null);
//Code that filters the data in the datatable and attaches the table to
the grid. Also does other misc. stuff
}

All the bit referred to in the comment needs to be in another method -
the one called by the callback delegate.
 
Hi,

rather than using delegates and BeginInvoke to do this, you could try using
the ThreadPool class, which has a method QueueUserWorkItem which runs a
method in a separate thread. Inside that method you can call the Browse
method synchronously and then do whatever you need to do once it has
finished running:

ThreadPool.QueueUserWorkItem(new WaitCallBack(BrowseStub));

private void BrowseStub(object state)
{
Browse();
// many more lines of code that cannot be executed
// while the BeginInvoke is running
}

cheers,
Michael
 
The problem I'm having with QueueUserWorkItem is that (similar to
Thread.Start), the code after my Browse() handles objects that were created
in the UI thread (ie. datagrid.datasource). And I can't manipulate objects
in this thread that were created by another thread .
 
VM said:
The problem I'm having with QueueUserWorkItem is that (similar to
Thread.Start), the code after my Browse() handles objects that were created
in the UI thread (ie. datagrid.datasource). And I can't manipulate objects
in this thread that were created by another thread .

Well if your objects aren't thread-safe, you need to make sure they're
only created in the right thread to do the work. That's going to be a
fundamental problem for you, almost regardless of how you do the
multithreading itself. (Although it may mean you need to avoid the
threadpool and have your own queuing system, as you can't guarantee
which thread you'll get on the threadpool.)
 
In my case the only process that'll be running in a different thread is
filling the table. After that's run, I can attach the grid to the table in
the UI thread. The problem I'm having is that, with BeginInvoke, I reach the
datagrid.datasource code before the BeginInvoke has finished generating the
table. And I can't execute it synchronously because the user'll think that
the application froze when it's actually just filling the table with data.
Isn't there a moderately easy way to do something like this?

private delegate void loadAuditBrowseHandler();
private void btn_filter_Click(object sender, System.EventArgs e)
{
loadAuditBrowseHandler thisLoadAuditBrowseHandler = new
loadAuditBrowseHandler(this.Browse); //Browse fills the table only
thisLoadAuditBrowseHandler.BeginInvoke(null,null);
/* Code for this: While BeginInvoke is running Do Nothing
/* When BeginInvoke ends, then datagrid.datasource = datatable
created during Browse execution
}
 
VM said:
In my case the only process that'll be running in a different thread is
filling the table. After that's run, I can attach the grid to the table in
the UI thread. The problem I'm having is that, with BeginInvoke, I reach the
datagrid.datasource code before the BeginInvoke has finished generating the
table. And I can't execute it synchronously because the user'll think that
the application froze when it's actually just filling the table with data.
Isn't there a moderately easy way to do something like this?

private delegate void loadAuditBrowseHandler();
private void btn_filter_Click(object sender, System.EventArgs e)
{
loadAuditBrowseHandler thisLoadAuditBrowseHandler = new
loadAuditBrowseHandler(this.Browse); //Browse fills the table only
thisLoadAuditBrowseHandler.BeginInvoke(null,null);
/* Code for this: While BeginInvoke is running Do Nothing
/* When BeginInvoke ends, then datagrid.datasource = datatable
created during Browse execution
}

Yes, and it's as I've said before - you provide a callback to
BeginInvoke, which you then use to fire another delegate on the UI
thread which can then do datagrid.DataSource = datatable;

(Alternatively, make the other delegate fire at the end of Browse, and
avoid the extra callback.)
 
Hi,

maybe I'm missing something here, but how does calling a delegate keep the
code in the "UI thread"? If you call a delegate by using BeginInvoke it uses
the framework threadpool (the same one used in ThreadPool.QueueUserWorkItem
AFAIK), and if you just call it directly it will use the same thread as the
caller...?

Provided you are aware of the threading issues and use locks or other thread
control techniques as necessary, there's no reason why you can't set the
datagrid's datasource property in the threadpool thread, which should get
the desired effect?

cheers
Michael
 
Well, it works (more or less).

The only major problem I'm having is that the callback dows not execute the
whole attachToGrid2 method. It'll run but will stop at
"dataGrid_browse.DataSource = DataView_Filter;". Once it gets there it'll go
back to the top of the method and execute everything over again (displaying
the first MessageBox). If I comment "dataGrid_browse.DataSource =
DataView_Filter;", it'll display the 2nd MessageBox (and won't execute
everything twice). Why does it do this when I set the datasource? Am I
running into some internal error?

IAsyncResult i = thisLoadAuditBrowseHandler.BeginInvoke(new
AsyncCallback(attachToGrid2),null);

private void attachToGrid2(IAsyncResult obj)
{
DataView DataView_Filter;
int iCount = 0;
MessageBox.Show ("Run " + _dataTable_AddrRec.Rows.Count.ToString());
ZMMatch.Query ZMQuery = new ZMMatch.Query();
DataView_Filter = new DataView(_dataTable_AddrRec);
dataGrid_browse.DataSource = DataView_Filter;
MessageBox.Show ("Why can't I see this MessageBox? I only see it when I
comment previous line"); //NEVER REACHES HERE
}

Thanks again.
 
Michael O'Brien said:
maybe I'm missing something here, but how does calling a delegate keep the
code in the "UI thread"? If you call a delegate by using BeginInvoke it uses
the framework threadpool (the same one used in ThreadPool.QueueUserWorkItem
AFAIK), and if you just call it directly it will use the same thread as the
caller...?

You need to specifically fire it in a way that gets it to execute on
the UI thread, using Control.Invoke or Control.BeginInvoke.
Provided you are aware of the threading issues and use locks or other thread
control techniques as necessary, there's no reason why you can't set the
datagrid's datasource property in the threadpool thread, which should get
the desired effect?

Yes there is - a golden rule of Windows Forms is that you *mustn't*
touch the UI from any thread other than the thread which created it. It
*may* work if you're lucky, but sooner or later it won't.
 
VM said:
Well, it works (more or less).

The only major problem I'm having is that the callback dows not execute the
whole attachToGrid2 method. It'll run but will stop at
"dataGrid_browse.DataSource = DataView_Filter;". Once it gets there it'll go
back to the top of the method and execute everything over again (displaying
the first MessageBox). If I comment "dataGrid_browse.DataSource =
DataView_Filter;", it'll display the 2nd MessageBox (and won't execute
everything twice). Why does it do this when I set the datasource? Am I
running into some internal error?

IAsyncResult i = thisLoadAuditBrowseHandler.BeginInvoke(new
AsyncCallback(attachToGrid2),null);

private void attachToGrid2(IAsyncResult obj)
{
DataView DataView_Filter;
int iCount = 0;
MessageBox.Show ("Run " + _dataTable_AddrRec.Rows.Count.ToString());
ZMMatch.Query ZMQuery = new ZMMatch.Query();
DataView_Filter = new DataView(_dataTable_AddrRec);
dataGrid_browse.DataSource = DataView_Filter;
MessageBox.Show ("Why can't I see this MessageBox? I only see it when I
comment previous line"); //NEVER REACHES HERE
}

attachToGrid2 is still running on a threadpool thread, and so you
mustn't touch the UI on it. You need to use Control.Invoke or
Control.BeginInvoke with another delegate to get back to the UI thread.
 
Back
Top