ThreadStart and ThreadPool

  • Thread starter Thread starter Franz
  • Start date Start date
William Stacey said:
Not that I can see.

Making things which are logically method parameters *have* to be the
start of a type leads to designs being bent out of shape, IMO. Imagine
if you had to do that for *every* method call?
Never said it was. I agreed it would be good to have.


Same reason it works in the thread pool. BeginInvoke uses a thread pool
thread to run the delegate IIRC. However the thread was created
already so it can call any delegate with args as normal.

I don't see how that's relevant at all, to be honest. There's no
*technical* reason why it shouldn't take an object, as far as I can
see.
I don't see it more convenient then your anonymous example. You then need
to create another delegate.

No, there'd be no need to create another delegate, unless you want it
strongly typed, of course.

The problem with the anonymous example is that it doesn't work with C#
today, and the code is less clear than it would be if we could just say
new Thread (new ThreadStart(SomeMethod)).Start(someParameter);
 
John Saunders said:
How often have you wanted to pass only a single object as a parameter? That
is, a single object which is not an inherent part of the processing to be
performed by the thread?

Reasonably often. More often, it makes sense to have all the state in
one class, but have that class itself *just* be state - it doesn't
logically have to know what to *do* with that state.
I've never found it a burden to place all the state in the class which
contains the thread's ThreadStart method. It has always created a cleaner
design.

It's not a huge burden, but it can be a convenience.
BTW, you can set the Name property of a thread after creating it in order to
tell it which number it is.

Not sure where this comment came from...
 
Jon Skeet said:
Reasonably often. More often, it makes sense to have all the state in
one class, but have that class itself *just* be state - it doesn't
logically have to know what to *do* with that state.

The only time I've used a separate state is in the case of a chain of
asynchronous methods, and even there I could have used a class which is the
"thread".

If the thread is an asynchronous action or process, and if the state defines
the current state of that process, then why not have the state be the data
of a class which encompasses the thread?

Note that I've done this since before the early days of Posix threads, back
when it wasn't tested with C++ at all. It's always made my life simpler. The
ability to put locking code in a property accessor has been invaluable.
It's not a huge burden, but it can be a convenience.


Not sure where this comment came from...

You or someone else mentioned the need to tell a thread which number it was.
 
John Saunders said:
The only time I've used a separate state is in the case of a chain of
asynchronous methods, and even there I could have used a class which is the
"thread".

If the thread is an asynchronous action or process, and if the state defines
the current state of that process, then why not have the state be the data
of a class which encompasses the thread?

Because fairly often in my experience yuou already *have* another class
which knows how to do the work, and that has its own state. Maybe we've
just done fairly different things :)
Note that I've done this since before the early days of Posix threads, back
when it wasn't tested with C++ at all. It's always made my life simpler. The
ability to put locking code in a property accessor has been invaluable.

True. If only we could also hide the variable backing the property
entirely from anything other than the property...
You or someone else mentioned the need to tell a thread which number it was.

Ah yes, found it now. Not sure I'd really want to use the name for that
(although you could also use it as state, if you only wanted to pass in
a string - aargh!) but I at least see why you mentioned it now :)
 
Making things which are logically method parameters *have* to be the
start of a type leads to designs being bent out of shape, IMO. Imagine
if you had to do that for *every* method call?

You don't have a chance to do that. You can set in propery or start method,
then your thread runs until it is done. You don't keep calling methods on
it. You can set (using locking) vars that the thread checks in its loop
such as stop, pause, etc. But you can do that using all the ways, the class
just contains this for you to make it clean.
I don't see how that's relevant at all, to be honest. There's no
*technical* reason why it shouldn't take an object, as far as I can
see.

It is relevant because that is how it works. The point was because it was
using the thread pool (hence an existing thread) it can call any delegate
and pass and object. You can't do when starting the thread, but you can do
if the thread is already started and you point it to do stuff. I don't see
a techinical reason it can't be done either in the ThreadStart delegate, but
would like to know why they did that also.

Cheers.
 
If you divide your program into function, then it makes sense. You create a
Worker class that does this work. It is a black box that does the work.
You can start it and stop it using methods that do the right thing. Start
and Stop alone are enouph for me to want to contain all my worker threads in
a class. Add enumerations and counters as properties and using a class
becomes even more attrative as you don't have all that twisted into your
other code, you have it all orginized in a class. You can also just create
another instance of the class to get another thread and start it with
optional parms, etc. But, this just an opinion, obviously everyone is free
to do what they like.
 
William Stacey said:
You don't have a chance to do that. You can set in propery or start method,
then your thread runs until it is done. You don't keep calling methods on
it. You can set (using locking) vars that the thread checks in its loop
such as stop, pause, etc. But you can do that using all the ways, the class
just contains this for you to make it clean.

I think we're miscommunicating. I'm saying that if setting state on
something and then calling a parameterless method were a good way of
doing things, we wouldn't have method parameters at all.
It is relevant because that is how it works. The point was because it was
using the thread pool (hence an existing thread) it can call any delegate
and pass and object. You can't do when starting the thread, but you can do
if the thread is already started and you point it to do stuff. I don't see
a techinical reason it can't be done either in the ThreadStart delegate, but
would like to know why they did that also.

Again, I think we're miscommunicating. I'm saying I wish it were
different, with ThreadStart taking a parameter, but you seem to be
saying it's different because it's different. I *know* that currently
everything to do with the thread pool takes a parameter and that
starting a new thread doesn't - I was asking what the reasoning was,
why the fact that it's a thread pool thread *should* change anything.
 
William Stacey said:
If you divide your program into function, then it makes sense. You create a
Worker class that does this work. It is a black box that does the work.
You can start it and stop it using methods that do the right thing. Start
and Stop alone are enouph for me to want to contain all my worker threads in
a class. Add enumerations and counters as properties and using a class
becomes even more attrative as you don't have all that twisted into your
other code, you have it all orginized in a class. You can also just create
another instance of the class to get another thread and start it with
optional parms, etc. But, this just an opinion, obviously everyone is free
to do what they like.

But you *have* to create another instance of the class just to start it
with different parameters. In other aspects of programming, you can
keep state in the class for things which *don't* need to be different
for different method calls, and pass in the "variable" part as
parameters. I don't see why threading fundamentally *needs* to use an
"everything is in the object state" model.
 
Don't you guys know any people on the compiler team who implemented this to
ask the question?
Seems like we running around in circles with nobody really knowing why a
thread cannot take an object. Let's just ask someone who may
know...like...richter or one of the compiler guys with blogs all over the
place.
 
But you *have* to create another instance of the class just to start it
with different parameters.

Correct - that is a benefit I think. This is a good reason to contain state
in a class to begin with. You can have each worker with its own state and
all the methods that work on that thread contained within the class also -
std OO stuff. If you don't use a class, you end with state vars in some
class anyway (i.e. your class that is starting the threads.)
 
Jon Skeet said:
Because fairly often in my experience yuou already *have* another class
which knows how to do the work, and that has its own state. Maybe we've
just done fairly different things :)

It just seems non-OO for the class which knows how to do the work to not
have the state necessary to get the work done. Consider that if you change
the work which needs to be done, you'd have to change a separate state
class! Also, why should the state be visible to anything other than the
class which knows what to do with it?
 
John Saunders said:
It just seems non-OO for the class which knows how to do the work to not
have the state necessary to get the work done.

Do you never pass parameters to things then?
Consider that if you change the work which needs to be done, you'd have to
change a separate state class! Also, why should the state be visible to
anything other than the class which knows what to do with it?

I'll use an example I'm working with at the moment. We have one class,
the "client" class, which knows which host to connect to, which port
etc. We have another class which represents the contents of the request
to send to the host. As we use the same host information pretty much
throughout the application, it would seem silly to recreate that for
every request.

Now, it's the client class which knows how to connect and send data -
it just asks the request class what data should be sent. It would
therefore make sense to be able to do something like:

new Thread (new ThreadStart(client.Send)).Start(request);

See what I mean?
 
Don't you guys know any people on the compiler team who implemented this to
ask the question?

It's not really a compiler question - it's a framework question.
Seems like we running around in circles with nobody really knowing why a
thread cannot take an object. Let's just ask someone who may
know...like...richter or one of the compiler guys with blogs all over the
place.

I know a couple of people in the C# team, "compiler guys" if you will -
but I suspect they didn't have much to do with the design of the
threading libraries.
 
William Stacey said:
Correct - that is a benefit I think. This is a good reason to contain state
in a class to begin with. You can have each worker with its own state and
all the methods that work on that thread contained within the class also -
std OO stuff. If you don't use a class, you end with state vars in some
class anyway (i.e. your class that is starting the threads.)

You force the worker and the state to be in the same class though, or
go through another level of indirection. You're also forced to create a
new instance of the *worker* even if it's using the same information as
other threads doing the same kind of work - unless you go through
another level of indirection.
 
I think we're miscommunicating. I'm saying that if setting state on
something and then calling a parameterless method were a good way of
doing things, we wouldn't have method parameters at all.

Not saying it is good - we agree. Just saying how to get around it and the
method of using a class is better anyway to contain the thread and its state
and methods. That is me. You don't agree and that is fine (no problems.)
Again, I think we're miscommunicating. I'm saying I wish it were
different, with ThreadStart taking a parameter,

Me too.
saying it's different because it's different. I *know* that currently
everything to do with the thread pool takes a parameter and that
starting a new thread doesn't - I was asking what the reasoning was,
why the fact that it's a thread pool thread *should* change anything.

Related but different question I think. The thread pool does not really
change that model - it just abstracts it at a higher level. Same as you
could do yourself.

//Pseudo Code
public class ThreadPool
{
public RunThis(delegate del, object value)
{
Q.Enqueue(new ThreadState(del, value));
}
// ThreadStart delegate. Started by the thread pool.
private ThreadEntry()
{
// Entry point of a thread pool thread.
while(true)
{
threadState = dequeueNextWorkItem(); // block
for work items;
threadsWorking++;
threadState.Delegate(threadState.StateObject); // run
callback delegate passing state value.
//any clean up for this thread pool thread.
}
}
}

You could do the same without the thread pool to allow calling your thread
with object.
 
You force the worker and the state to be in the same class though,

Right. Good thing imo. State could be a ref to shared object too as you
know.
You're also forced to create a
new instance of the *worker* even if it's using the same information as
other threads doing the same kind of work

So? That is what I want anyway. Each worker is its own object. That way I
can call Stop() on each objects and it does the right thing, etc. They can
all use the same shared object or other depending on your needs, does not
matter (assuming we do right thing with locking, etc.)
 
Here is another way to abstrate thread start to allow passing the del and an
object. This takes a MethodDelegate, but you could change to take other
delegates or more parms, etc. Not sure if the test for IsAlive is enouph to
make sure the delegate was already called before we release the lock to
allow other StartThread calls, but I think it is (if not we could do
something else.)

private void button47_Click(object sender, System.EventArgs e)
{
Thread t = StartThread.Start(new MethodDelegate(MyMethod), "Hello");
}

private void MyMethod(object value)
{
string s = value as string;
Console.WriteLine("Ran MyMethod and passed:"+s);
}

public delegate void MethodDelegate(object value);
public class StartThread
{
private static object state;
private static MethodDelegate threadDel;
private static readonly object syncLock = new object();
private StartThread()
{
}
public static Thread Start(MethodDelegate del, object value)
{
if ( del == null )
throw new ArgumentNullException("del");
lock(syncLock)
{
threadDel = del;
state = value;
Thread t = new Thread(new ThreadStart(ThreadStart));
t.Start();
while( ! t.IsAlive )
Thread.Sleep(0);
return t;
}
}
private static void ThreadStart()
{
threadDel(state);
}
}
 
William Stacey said:
Right. Good thing imo. State could be a ref to shared object too as you
know.

It could be - but it could also end up bending the design unnaturally.
So? That is what I want anyway. Each worker is its own object. That way I
can call Stop() on each objects and it does the right thing, etc. They can
all use the same shared object or other depending on your needs, does not
matter (assuming we do right thing with locking, etc.)

That's fine for when that's what you want - it's no use when it's *not*
what you want, however: when you want all workers treated together, or
when you have no desire to be able to stop the workers anyway. To take
my earlier example situation, you can certainly give the "request"
object the "client" object which should be used to send it, and then
either move all the sending code into the request or make the request
call the client asking it to send it, but it's less natural, IMO, than
asking the client (of which I only *want* one instance) to send the
request (which will vary).
 
Jon Skeet said:
Do you never pass parameters to things then?

Yes. Class constructors! :-)
I'll use an example I'm working with at the moment. We have one class,
the "client" class, which knows which host to connect to, which port
etc. We have another class which represents the contents of the request
to send to the host. As we use the same host information pretty much
throughout the application, it would seem silly to recreate that for
every request.

Now, it's the client class which knows how to connect and send data -
it just asks the request class what data should be sent. It would
therefore make sense to be able to do something like:

new Thread (new ThreadStart(client.Send)).Start(request);

See what I mean?


I would have created a nested class (Client.Sender) within Client to handle
the sends. Request would be a parameter to the constructor of that class.
Client.Sender would have a Start method which would create the thread and
start it. The ThreadStart method would be a private method of Client.Sender.

This way, Client.Sender represents the asynchronous task of sending data to
the host. Client knows the parameters (host, port), and the nested class can
get that from Client.
 
I would have created a nested class (Client.Sender) within Client to handle
the sends. Request would be a parameter to the constructor of that class.

It would also need a parameter to specify the client. Nested class
instances don't have implicit "parent" references.
Client.Sender would have a Start method which would create the thread and
start it. The ThreadStart method would be a private method of Client.Sender.

This way, Client.Sender represents the asynchronous task of sending data to
the host. Client knows the parameters (host, port), and the nested class can
get that from Client.

And would you have done it that way if there'd been a type-safe way of
creating a new thread which called a particular method within Client
with a specific parameter? (Being able to pass a single object gets
half way, but doesn't give type safety.) Would you have done it that
way if threads *hadn't* been involved?

This is what I mean by bending a design out of shape - I see no reason
for the extra class, logically.
 
Back
Top