How to use events to cross threads?

B

Bryce K. Nielsen

I have an object that starts a thread to do a "process". One of the steps
inside this thread launches 12 other threads via a Delegate.BeginInvoke to
process. After these 12 threads are launched, the main thread waits. At the
completion of each subthread, the mainthread checks all 12 thread objects to
see if they are done. If they are, raise an event that says we're done.

So, it's kinda like this:

ProcessThread
- Creates a ProcessObject
- ProcessObject Execute creates a list of 12 SubProcessObjects
- Invokes 12 new SubProcessThreads
- SubProcessObject executes
- SubProcessObject sets a "Completed" flag
- The end routine checks the SubProcessObject list to see if all
"Completed" flags
- If so, raise event to main process to continue to next ProcessObject

Problem is, I don't think the pick-up-on-main-thread is really executing on
the main thread. I think it's continuing to process on the newly created
subthread. Since my class has nothing to do with a Control, I don't have
access to InvokeRequired or Invoke. Is there a way ot make sure my
subthreads really complete?

-BKN
 
G

Guest

Using Delegate.BeginInvoke is called "Asynchronous Delegates". Using
asynchronous delegates uses threads from the thread pool. Unless all 12
delegate calls are active (i.e. they haven't completed by the time the other
delegates are called) it's more than likely an asynchronous delegate is
reusing a thread pool thread that was uses by another asynchronous delegate.

If you want to keep track of threads via a Thread object then you don't want
to use thread pool threads.

How are you keeping track of each thread?

Normally, if the continuation of one thread is Dependant on one or more
other threads you would call Thread.Join() for each thread object--something
you can't do with thread pool threads.
 
B

Bryce K. Nielsen

How are you keeping track of each thread?
The main process has a list of objects that are associated with each
thread/delegate. In this object is a flag set at the end of the process, and
then the AsyncCallback from the main process checks the list of processes to
see if they're all completed. If they are, continue processing, otherwise do
nothing and wait until it's called again by another completing thread.
Normally, if the continuation of one thread is Dependant on one or more
other threads you would call Thread.Join() for each thread
object--something
you can't do with thread pool threads.

Well, previously I had all my code on the Form executing stuff, so at the
end (after the AsyncCallback was called and all objects.Completed flag was
true) I would call Form.Invoke and this would continue processing back on
the original/Form thread. However, I recently decoupled the main process
from the Form onto it's own Thread object. So, since this object is not a
Control, it has no Invoke() method. So I'm afraid that at the end, the
process is picking up on the last-executed thread, instead of giving control
back to the main process thread.

So Thread.Join() would allow me to pick back up on the main thread?

-BKN
 
G

Guest

A general rule of thumb: if the concept/method exists in .NET 1.1 then the
callback/event-handler is being called using the background thread. This is
the case for asynchronous delegates, they're called using the background
thread, not the thread that invoked the delegate.

In .NET 2.0 you can use the AsyncOperationManager class to make sure async
callbacks are called using a particular thread.

Even if you call your AsyncCallback on the "main" thread, you're still going
to be in a situation where the thread that instigates the callback is still
technically running when the callback is called. In the case of async
delegates this callback means the method has finished executing; it doesn't
mean the thread has finished executing.

The only way to know if a thread has terminated (without polling) is if you
use Thread.Join, something that you can't use with thread pool threads. In
your case, I don't know why you'd want to make the distinction of when the
task is completed and when the thread has terminated...

Another thing I should point out is when to use multiple threads. There's
really only three reliable scenarios for multiple threads. One scenario is:
you want to spawn a single thread to do work because another thread that does
not use much of the CPU needs to be active. This includes spawning
background threads from a GUI thread to keep the GUI responsive. A second
scenario is if you want to take advantage of multi-processor or
multi-core-processors. The third scenario is you want to wait for something
to complete (like asynchronous IO) without blocking another thread.

If you spawn more than one thread on a single processor, single-core
processor, then you're actually slowing your program down. Your program is
now using more system resources, asking the system to manage your threads
(time-slicing between them, incurring a context switch), and adding
processing to start and wait for the threads. All of which take processing
time.
 
B

Bryce K. Nielsen

The only way to know if a thread has terminated (without polling) is if
you
use Thread.Join, something that you can't use with thread pool threads.
In
your case, I don't know why you'd want to make the distinction of when the
task is completed and when the thread has terminated...

That's kind of what I thought, that the "pick up" code is no longer being
executed on the main process thread, but is now executing on the last
completed delegate thread. I guess that's OK, it just didn't seem like the
right way to go and I'd rather code continue back on the original thread. As
mentioned before, I was able to do this when the code was using the Form
thread. But I guess I can't do that now.

-BKN
 
B

Bryce K. Nielsen

Another thing I should point out is when to use multiple threads. There's
really only three reliable scenarios for multiple threads. One scenario
is:
you want to spawn a single thread to do work because another thread that
does
not use much of the CPU needs to be active. This includes spawning
background threads from a GUI thread to keep the GUI responsive. A second
scenario is if you want to take advantage of multi-processor or
multi-core-processors. The third scenario is you want to wait for
something
to complete (like asynchronous IO) without blocking another thread.

That's actually the part of the process that I'm multithreading. The project
is migrating data from one SQL Server to another (and the client was adamant
about not using DTS). There are 12 tables that are being transferred, so I
decided to spawn 12 threads to do this transfer bit. A lot of pre-setup and
post-setup is necessary before and after the transfers, and that happens
synchronously on the main-process-thread. But having the SQL statements run
threaded like this does appear to go quite a bit faster (i.e. while a large
SELECT is running, the short ones are completing).

-BKN
 
G

Guest

Thread.Join will mean the main thread will block until all threads that have
been used in Thread.Join calls, terminate. Which will do what you want. If
that main thread is doing any GUI work; your program will look unresponsive
(I think you said you abstracted it away from a Form class; so, I assume this
"main" thread is another background thread, which I refer to as the "host"
thread).

I would suggest, if you're using VS2005/.NET2.0, that you use the
BackgroundWorker class. It uses the an updated asynchronous model (via
events, rather than a call back) and manages calling the events (at least the
progress and the completed events) on the thread that started the
asynchronous operation, not the thread that is performing the background
operation.

I don't think you need to worry whether the background thread is still
"alive", just whether it's operation is completed or not. The drawback of
using something like BackgroundWorker, as you've noticed with asynchronous
delegates, is you need some way of managing the state of all those
operations. This could simply be an array of WaitHandles for which a call to
WaitHandle.WaitAll is made in the host thread to tell when all operations
have completed. The BackgroundWorker.RunWorkerCompleted event would simply
signal that WaitHandle object.
 
G

Guest

Actually, after a bit of though, you can get the same effect as Thread.Join
with asynchronous delegates with the EndInvoke call. Each BeginInvoke should
be matched with an EndInvoke call anyway. EndInvoke will block (just as
Thread.Join blocks) until the delegate call returns.
 
B

Bryce K. Nielsen

Actually, after a bit of though, you can get the same effect as
Thread.Join
with asynchronous delegates with the EndInvoke call. Each BeginInvoke
should
be matched with an EndInvoke call anyway. EndInvoke will block (just as
Thread.Join blocks) until the delegate call returns.

I actually am calling EndInvoke, since the delegate returns a boolean value
and the only way to gain access to this value is via EndInvoke. But the
problem remains, the resulting process does not return to the original
thread, but continues on the last delegate's thread. I'm running into an
issue with my original Thread simply not existing anymore and so my
Thread.Suspend is erroring.

So back to my original question, even if I move away from using the thread
pool, how do I have ThreadA pick up when ThreadB stops?

-BKN
 
B

Barry Kelly

Bryce K. Nielsen said:
I actually am calling EndInvoke, since the delegate returns a boolean value
and the only way to gain access to this value is via EndInvoke. But the
problem remains, the resulting process does not return to the original
thread, but continues on the last delegate's thread. I'm running into an
issue with my original Thread simply not existing anymore and so my
Thread.Suspend is erroring.

So back to my original question, even if I move away from using the thread
pool, how do I have ThreadA pick up when ThreadB stops?

Sorry for butting in here, Bryce: I'm not certain what exactly you're
after, but how does this look, assuming no UI component, and assuming
..NET 1.1:

---8<---
using System;
using System.Threading;
using System.Windows.Forms;
using System.Collections;

class App
{
class Job
{
TimeSpan _delay;

public Job(TimeSpan delay)
{
_delay = delay;
}

public void Execute()
{
Thread.Sleep(_delay);
Console.WriteLine("Job done ({0})", _delay);
}
}

static void Main(string[] args)
{
ArrayList jobs = new ArrayList();
for (int i = 0; i < 10; ++i)
jobs.Add(new Job(TimeSpan.FromMilliseconds(50 + 50 * i)));

ArrayList threads = new ArrayList();
foreach (Job job in jobs)
{
Thread thread = new Thread(new ThreadStart(job.Execute));
thread.Start();
threads.Add(thread);
}

// Executing in background ...

// Wait for all jobs to finish...
foreach (Thread thread in threads)
thread.Join();

// Done.
Console.WriteLine("All done.");
}
}
--->8---

Alternatively, to alert the UI, how about this (for .NET 2.0):

---8<---
using System;
using System.Threading;
using System.Windows.Forms;

class App
{
static void Main(string[] args)
{
Form form = new Form();
Button start = new Button();
start.Parent = form;
start.Text = "Start";
start.Click += delegate
{
start.Enabled = false;
ThreadPool.QueueUserWorkItem(delegate
{
int runningCount = 10;
using (ManualResetEvent finished =
new ManualResetEvent(false))
{
for (int i = 0; i < 10; ++i)
{
TimeSpan delay =
TimeSpan.FromMilliseconds(50 + 50 * i);
ThreadPool.QueueUserWorkItem(delegate
{
Thread.Sleep(delay);
Console.WriteLine("Job done ({0})", delay);
if (Interlocked.Decrement(ref runningCount) == 0)
finished.Set();
});
}
finished.WaitOne();
Console.WriteLine("All done.");
// Fire an event on the UI thread.
start.Invoke((MethodInvoker) delegate
{
start.Enabled = true;
MessageBox.Show("Done!");
});
}
});
};

Application.Run(form);
}
}
--->8---

-- Barry
 
B

Bryce K. Nielsen

ArrayList threads = new ArrayList();
foreach (Job job in jobs)
{
Thread thread = new Thread(new ThreadStart(job.Execute));
thread.Start();
threads.Add(thread);
}

// Executing in background ...

// Wait for all jobs to finish...
foreach (Thread thread in threads)
thread.Join();

I think that's what I was missing. I was trying to figure out if I want to
launch 12 threads how to I join on all 12 in an asynchrnous manner.

This will take some time, changing from the delegate way to the Thread way,
but I'll try that and report back.

-BKN
 
B

Bryce K. Nielsen

I think that's what I was missing. I was trying to figure out if I want to
launch 12 threads how to I join on all 12 in an asynchrnous manner.

Question on this though, if I call Thread.Join(), will the Form's thread be
able to update a flag (allowing the user to cancel the process)?

In other words, I have a button on the Form to cancel the process. Right
now, that sets a flag on the main process object (which in turn sets it on
the 12 dataprocess objects). Each of these threads intermittently checks
this flag, and if it's set, rollback the process. But if I call
Thread.Join() on all 12 threads, will this flag be set, or will it wait
until Thread.Join has come back?

-BKNK
 
B

Barry Kelly

The first bit of code I posted, the one that loops through each thread
and calls Thread.Join(), will block until they're all finished, so the
body of the threads will be asynchronous, but the .Join() calls
themselves will be synchronous (i.e. the loop won't exit until all work
is done).
Question on this though, if I call Thread.Join(), will the Form's thread be
able to update a flag (allowing the user to cancel the process)?

In other words, I have a button on the Form to cancel the process. Right
now, that sets a flag on the main process object (which in turn sets it on
the 12 dataprocess objects). Each of these threads intermittently checks
this flag, and if it's set, rollback the process. But if I call
Thread.Join() on all 12 threads, will this flag be set, or will it wait
until Thread.Join has come back?

Thread.Join() blocks until the delegate passed to the Thread constructor
has returned. If the delegate passed to the Thread constructor exits in
a timely fashion after this flag has been set, it's no different:
Thread.Join() will still only continue after the delegate passed has
exited.

You haven't mentioned whether you're using 1.1 or 2.0. I've assumed the
worst, and here's a more complete sample (with UI) for 1.1:

---8<---
using System;
using System.Threading;
using System.Windows.Forms;
using System.Collections;
using System.ComponentModel;
using System.Drawing;

class App
{
class Job
{
TimeSpan _delay;
bool _cancelled;

public Job(TimeSpan delay)
{
_delay = delay;
}

public void Cancel()
{
_cancelled = true;
}

public void Execute()
{
DateTime finished = DateTime.UtcNow + _delay;
while (DateTime.UtcNow < finished)
{
Thread.Sleep(TimeSpan.FromMilliseconds(1));
if (_cancelled)
{
Console.WriteLine("Job {0} cancelled.", _delay);
return;
}
}
Console.WriteLine("Job done ({0})", _delay);
}
}

class OuterJob
{
ISynchronizeInvoke _invoker;
MethodInvoker _done;
ArrayList _jobs = new ArrayList();

public OuterJob(ISynchronizeInvoke invoker, MethodInvoker done)
{
_invoker = invoker;
_done = done;
}

public void Cancel()
{
lock (_jobs.SyncRoot)
foreach (Job job in _jobs)
job.Cancel();
}

public void Execute()
{
// Collect jobs.
lock (_jobs.SyncRoot)
{
for (int i = 0; i < 10; ++i)
_jobs.Add(
new Job(TimeSpan.FromMilliseconds(50 + 50 * i)));
}

// Start jobs running.
ArrayList threads = new ArrayList();
lock (_jobs.SyncRoot)
{
foreach (Job job in _jobs)
{
Thread thread =
new Thread(new ThreadStart(job.Execute));
thread.Start();
threads.Add(thread);
}
}

// Wait for all to finish.
foreach (Thread thread in threads)
thread.Join();

Console.WriteLine("All done.");
// Notify the UI or whatever.
if (_done != null && _invoker != null)
_invoker.Invoke(_done, null);
}
}

OuterJob _outerJob;
Thread _outerThread;
Form _mainForm;
Button _start;
Button _cancel;

void CancelClick(object sender, EventArgs e)
{
_outerJob.Cancel();
}

void StartClick(object sender, EventArgs e)
{
_start.Enabled = false;
_cancel.Enabled = true;
_outerJob = new OuterJob(_mainForm, new MethodInvoker(Done));
_outerThread = new Thread(new ThreadStart(_outerJob.Execute));
_outerThread.Start();
}

void Done()
{
_cancel.Enabled = false;
_start.Enabled = true;
_outerJob = null;
_outerThread = null;
}

static void Main(string[] args)
{
App app = new App();

app._mainForm = new Form();
app._start = new Button();
app._start.Parent = app._mainForm;
app._start.Text = "Start";
app._start.Click += new EventHandler(app.StartClick);

app._cancel = new Button();
app._cancel.Parent = app._mainForm;
app._cancel.Location = new Point(0, 50);
app._cancel.Text = "Cancel";
app._cancel.Enabled = false;
app._cancel.Click += new EventHandler(app.CancelClick);

Application.Run(app._mainForm);
}
}
-->8---

I wouldn't have gone this far, but I know you from the Delphi newsgroups
;-)

-- Barry
 
B

Barry Kelly

Barry Kelly said:
ISynchronizeInvoke _invoker;
MethodInvoker _done;
// Notify the UI or whatever.
if (_done != null && _invoker != null)
_invoker.Invoke(_done, null);

By the way, there's no error handling in the code I posted. You can use
this pattern (above) to invoke arbitrary code in the context of the UI,
in the case of errors. For example, one could use the same pattern with
a callback for any exceptions that occurred.

-- Barry
 
B

Bryce K. Nielsen

You haven't mentioned whether you're using 1.1 or 2.0. I've assumed the
worst, and here's a more complete sample (with UI) for 1.1:

I'm using 2.0, VS2005 C# to be exact.

I've moved away from using the Delegates (which I only used because a friend
of mine swore by them, I shoulda just stuck with the original threading
model to begin with) which I think is doing what I want it to do now. It's
also cleaned up my code a lot.
I wouldn't have gone this far, but I know you from the Delphi newsgroups
;-)

Heh, thanks. You need to stop using _name and start using FName then ;-)

Oh, and I have to say I prefer Delphi's TThread way of doing things a lot
more than DotNets. For some reason being able to encapsulate all my code
inside the "thread" object just feels better.

-BKN
 
B

Barry Kelly

Bryce K. Nielsen said:
I'm using 2.0, VS2005 C# to be exact.

I've moved away from using the Delegates (which I only used because a friend
of mine swore by them, I shoulda just stuck with the original threading
model to begin with) which I think is doing what I want it to do now. It's
also cleaned up my code a lot.

It can be easier to work with when you've got explicit objects you can
manipulate. The only problem is resources: you can run out of threads.
Every thread reserves a megabyte of virtual address space or so for its
stack, so you don't need too many threads before you start running into
trouble. That's where the ThreadPool, a synchronization object like
ManualResetEvent, and a little fiddling with Interlocked.* becomes the
better approach - an earlier post of mine used the ThreadPool.
Alternatively, a producer-consumer queue can work, with Semaphores.

Of course, it's possible to get into a deadlock by running out of
ThreadPool threads, but that's a different problem.
Heh, thanks. You need to stop using _name and start using FName then ;-)

Oh, and I have to say I prefer Delphi's TThread way of doing things a lot
more than DotNets. For some reason being able to encapsulate all my code
inside the "thread" object just feels better.

And I have to say that I prefer passing an anonymous delegate to
ThreadPool.QueueUserWorkItem()... ;) I lurve anonymous delegates.

If you're nostalgic for TThread, it's easy to create, of course.

-- Barry
 

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

Top