Threading question

J

John

I've got some reasonably complex business logic in my C# code, in a class
called by a ASP.NET page. This takes around 3-4 seconds to execute. It's not
dependent on SQL calls or anything like that.

I know of the problems with the worker process doing two things at once from
the main thread pool (The old "Sit in a tight loop for 20 seconds and watch
all your webapps die!" issue). So I've worked around the issue by spawning
the business logic into a new thread. The main thread Sleep()'s until it's
finished, and then continues. eg:

Button_Click:
1) start new thread
2) Sleep until it's done
3) destroy it

New thread:
1) Do something heavy for a few seconds
2) terminate

I figured that, since the main thread is sleeping waiting for my new thread,
I don't need to lock() any objects, I can just access then directly.

It all seems to work fine, and has solved the strange delays my other
webapp-users were experiencing.

Does this sound like good practice?

Thanks,

John
 
S

Scott Allen

Hi John:

I'm not familiar with "sit in a tight loop for 20 seconds and watch
the webapps die" issue. That is an issue for WinForms applications
which need a main thread to pump messages to keep the GUI responsive,
but there in no 'main thread' in a web app.

I think you are making the app more complicated than it needs to be by
using two threads to process a single request instead of one thread. I
don't understand why your users would see strange delays without the
extra thread. Does the extra thread perform any work to produce
results that are sent to the client? How do you know you are sleeping
long enough for the worker thread to finish? Are you using EndInvoke
or a reset event?
 
K

Kevin Spencer

What you've designed is threading without any of the benefits of threading.
You create a thread, and then sleep the main thread until it is finished.
This is exactly equivalent to not creating a new thread at all. The purpose
of threading is to allow 2 different tasks to be executed at the same time.

--
HTH,
Kevin Spencer
..Net Developer
Microsoft MVP
I get paid good money to
solve puzzles for a living
 
J

John

Hi Scott,

I'm using Windows 2000 and XP, don't know if it's fixed on 2003. .NET 1.1
sp1.

Add a button to your webform, and on the server event handler put something
like:

DateTime d = DateTime.Now.AddSeconds(20);
while (DateTime.Now < d) ;


Run the form, click the button. See your CPU jump to 100%, and try to use
another webapp in another browser window. Nothing. Dead. Stuffed :) At least
until 20 seconds have passed.

But if you run the code I suggested, other apps remain responsive for this
20 second wait.... strange! even though CPU is at 100%, because (I assume)
when the other thread of same priority asks for time, it gets 50% of the
slots the busy-wait thread was getting.

I know it's been discussed at length in the past. I'm aware sitting in a
tight loop for 20 seconds is a stupid thing to do ;) but in my real-life app
I'm number-crunching with datasets. For maybe 2-4 seconds.

I check the new thread is finished using Thread.Join() with a timeout
figure. I guess you could also just Sleep(200) or something and poll
ThreadStatus.

My concerns are two-fold:

1) there might be some weird issue with sync locking, even though *I'm* not
using them in two threads simultaniously, maybe the system does somehow??

2) I guess the system has a thread-pool for a good reason, whereas I'm just
creating threads on demand. Maybe I could overload the system??

Thanks,

John
 
J

John

Thanks for the reply Kevin,

Try writing a button click event handler (ASP.NET) that does:

private void Button_Click()
{
DateTime d = DateTime.Now.AddSeconds(20);
while (DateTime.Now < d) ;
}

Then try something like:

private void AnotherButton_Click()
{
Thread th = new Thread(new ThreadStart(DoSomething));
th.Start();
th.Join();
}

private void DoSomething()
{
DateTime d = DateTime.Now.AddSeconds(20);
while (DateTime.Now < d) ;
}

Try to use other webapps while these are running.... weird!

It's been discussed at length before (before anyone tells me it's stupid to
sit in a tight loop for 20 seconds!!!).

In my real app I'm crunching numbers (entirely in C#, no SQL involved at
this stage). It only takes a couple of seconds (no chance of browser
timeout) but there seems to be a noticable performance change for other .NET
stuff on the server.

Thanks,

John
 
K

Kevin Spencer

Hi John,

I have used multi-threading in enterprise-level ASP.Net apps with no
problems whatsoever. However, that wasn't my point. My point was that your
use of Threading is equivalent to not using Threading at all, except for the
extra overhead. Your app will respond in almost exactly the same time as if
you hadn't used Threading at all.

A program has a main execution thread, which is sequential. It executes
exactly ONE instruction at a time. When a function is called, execution
jumps to the function, and continues until the function exits, and the
execution moves back to the calling function. Your technique does exactly
that, but in a more complex manner. You spawn a thread, and put the current
thread to sleep until it is finished processing.

Think of it this way. You are a thread, sitting at home, going about your
business, let's say, cleaning house. Now, you need to buy something from the
store.

Non-threaded: You go to the store to buy something. When you're done, you
come home and go back to work.

Your Threaded: You send your wife to the store to buy something. While she's
gone, you take a nap. When she gets back, you go back to work.

Properly Threaded: You send your wife to the store to buy something, while
you continue working. When she gets back, you continue working.

Note that the outcome of the first 2 situations is the same. You might just
as well have gone to the store yourself. You accomplished the same amount of
work in the time that you would have if you hadn't sent her at all.

Okay, now imagine that you're not a thread, but a string. While your wife is
gone to the store, you muss your hair until it is all matted and knotty.
When your wife gets home, she doesn't recognize you, and cries "I thought
you were a string!" You reply "I'm a frayed knot." ;-)

--
HTH,
Kevin Spencer
..Net Developer
Microsoft MVP
I get paid good money to
solve puzzles for a living
 
J

John

LOL! the old ones are always the best!

But my example demonstrates why I'm using a new thread - I don't want to do
two things at once in the same page in this instance, I just want to allow
other apps (and users on the same app) to run while my page is busy for a
few seconds.

My code seems to fix the problem demonstrated in the example. I think ur
saying it's ok, and safe code! Just checking.

John
 
B

bruce barker

your code is safe, it just takes more server resources without any benefit.
it no way makes the app quicker.

the reason you cannot run another web app is because IE limits itself to 2
connections to a server no matter how many browser instances you have. the
other broswers are waiting for the request to complete, not the cpu to go
down.

-- bruce (sqlwork.com)
 
S

Scott Allen

These two strings walk into a bar.

The first string says, "Bartender, I'd like a beer".
The second string says "Bartender, I'd like a beer too adfj ack$#
ickmd podsck".

The first string says, "Bartender, you'll have to excuse my friend, he
is not null terminated".

Haha. Oops, I thought this was the C++ newsgroup...

Anyway, John: I didn't see a noticeable difference between your two
examples in my environment. Are you running in release mode and a
release compile?
 
J

John

Hi Scott,

Release mode: you mean just setting the combo-box in VS from 'Debug' to
'Release', and changing web.config to debug="false"?? Then recompiling? (ok,
I know the web.config thing is JIT and doesn't need a recompile...)

Did that for both projects. Same behaviour. On 3 seperate machines. You have
SMP on yours? Maybe that helps matters - provided you don't have more
busy-threads than you have processors of course!

Thanks,

John
 
J

John

Well, on my systems there is an obvious benefit. It doesn't improve the
speed for person A, but it does stop him freezing out people B, C and D for
a few seconds every time he initiates the business logic!

It's quite easy to demonstrate in the code I've posted. Tested on 3 seperate
servers.

I don't think your idea about IE being the source of my problems is
correct - otherwise why would the 2-thread version work fine, but the
1-thread version doesn't?? Same test scenario. Same apparent behaviour for
person A (the person initiating the long request). Very different behaviour
for other users of the server.

Thanks,

John
 
G

Guest

Someone from Microsoft might want to jump in here and correct me if my guess
is wrong.

I can explain the behavior, but I'm not sure if this explanation is accurate
or not. This would explain 100% both the "lock while work is going on" and
the reason the thread + sleep combination works. However, it makes an
assumtion about the working process' implementation that I do not know is
valid.

IIS uses fibers. A fiber is a unit smaller than a thread that cooperatively
multitasks within a thread. The thread can be pre-empted, but a fiber within
the thread cannot. A fiber runs until it executes a blocking operation,
yields or completes.

A fiber is like a thread in how it works -- you write a function (delegate)
and start up the fiber. The fiber runs in the current thread's time slice
until it hits a blocking operation, when it hits a blocking operation another
fiber in the current thread is selected and given time.

You use this a lot in TCP protocols. If you start a thread for each user,
then you consume a lot of system resources and realy slow down the computer a
lot. If you use a single thread, then you have to do a lot of work keeping
straight which user is which. So you have a choice between simple and
elegant code (threading) or code that is faster, but that requires a lot more
work.

Fibers are an "inbetween" approach. Fiber code looks just like multiple
threads, but consumes resources as if it were single threaded. The code runs
exclusively within its thread until it hits a blocking operation, then
another block of code within the thread is given time.

The issue is if the fiber never hits a blocking operation, it starves the
other fibers.

You don't want to sleep, in this case, because sleep just yields to the next
fiber or thread. You want to join your thread you launched, which should
have the same effect but ensures the order in which events occur. I would
not want to just sleep, I would want to call Thread.Join.

The alternative approach is that you add a Sleep(0) call periodically in the
work that you're doing, in order to give other fibers a chance to execute.

John said:
Hi Scott,

Release mode: you mean just setting the combo-box in VS from 'Debug' to
'Release', and changing web.config to debug="false"?? Then recompiling? (ok,
I know the web.config thing is JIT and doesn't need a recompile...)

Did that for both projects. Same behaviour. On 3 seperate machines. You have
SMP on yours? Maybe that helps matters - provided you don't have more
busy-threads than you have processors of course!

Thanks,

John
 
S

Scott Allen

Hi Dave:

That's an interesting thought. AFAIK, though, SQL Server is the only
product to use fibers. The ASP.NET runtime executes in a process
outside of IIS, and I've never read about it using fibers either, do
you have a source of info?

--
Scott
http://www.OdeToCode.com/


Someone from Microsoft might want to jump in here and correct me if my guess
is wrong.

I can explain the behavior, but I'm not sure if this explanation is accurate
or not. This would explain 100% both the "lock while work is going on" and
the reason the thread + sleep combination works. However, it makes an
assumtion about the working process' implementation that I do not know is
valid.

IIS uses fibers. A fiber is a unit smaller than a thread that cooperatively
multitasks within a thread. The thread can be pre-empted, but a fiber within
the thread cannot. A fiber runs until it executes a blocking operation,
yields or completes.

A fiber is like a thread in how it works -- you write a function (delegate)
and start up the fiber. The fiber runs in the current thread's time slice
until it hits a blocking operation, when it hits a blocking operation another
fiber in the current thread is selected and given time.

You use this a lot in TCP protocols. If you start a thread for each user,
then you consume a lot of system resources and realy slow down the computer a
lot. If you use a single thread, then you have to do a lot of work keeping
straight which user is which. So you have a choice between simple and
elegant code (threading) or code that is faster, but that requires a lot more
work.

Fibers are an "inbetween" approach. Fiber code looks just like multiple
threads, but consumes resources as if it were single threaded. The code runs
exclusively within its thread until it hits a blocking operation, then
another block of code within the thread is given time.

The issue is if the fiber never hits a blocking operation, it starves the
other fibers.

You don't want to sleep, in this case, because sleep just yields to the next
fiber or thread. You want to join your thread you launched, which should
have the same effect but ensures the order in which events occur. I would
not want to just sleep, I would want to call Thread.Join.

The alternative approach is that you add a Sleep(0) call periodically in the
work that you're doing, in order to give other fibers a chance to execute.
 
G

Guest

Hi Scott,

No inside source.

Keep in mind I've been running Windows a very long time. I have a copy of
Windows 1.04, 2.0, 3.0, 3.1, 3.11, 3.11wg (and yes the last two are subtlely
different), OS/2 3, OS/2 4, 95a, 98a, 98se, Me, 2000 and XP. I've written
and tested software on all versions of NT, including 3.1, as well, and have
written a small amount of Windows software for a very, very long time.

On OS/2, I used multithreading to take a program that was crawling along at
less than half the speed of the Windows version to get it running at 3x the
speed of the Windows version, so I'm very familiar with multithreading and
multithreaded design.

This is just a guess as to how the system operates, without investigating
with a debugger, all I can do is guess by observing behavior. I can provide
two explanations as to how IIS would block on ASP.NET requests, which it can
be verified that it does. As fibers are a "Microsoft Recommended" mechanism
for dealing with out of process blocking events, such as COM calls, I think
it is likely that fibers probably are in play here in some way, shape or
form, but no, I cannot prove that.

The best two explanations that I can think of are the fiber approach I
posted earlier. This seems likely as one of the goals with XP was to improve
IIS's throughput, and everyone knows how much fiber helps with throughput...
just kidding, but seriously, with TCP based protocols (such as TDS, which you
mention, and HTTP), fibers can improve performance a lot.

The other explanation is that aspnet_wp is invoked via COM, and is
configured to only allow a single COM request at a time. This would be a
spectacularly bad design, contrary to most of Microsoft's stated design goals
-- however Microsoft's been known to use poor design now and again (Windows
9x, Windows Me, need I continue...), and so it can't be ruled out.

In the case of a poorly implemented out of process COM control, what would
happen is that the parent's post would start a thread, and then return. When
control returned to the IIS process, IIS would be able to process the next
request as the thread continues its work, and the sleep statement would be
irrelevant (and could be removed).

However, this could be easily verified. Just do Sleep(20000), if you get a
pause for 20 seconds, then you know it isn't fibers. If the computer
continues, then you know it is fibers. Likewise, you can pop a sleep in a
loop and see what happens.
 

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

Members online

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,040
Latest member
papereejit

Latest Threads

Top